diff options
Diffstat (limited to 'Plugins/jingle')
290 files changed, 67548 insertions, 0 deletions
diff --git a/Plugins/jingle/Docs/jingle_changelog.txt b/Plugins/jingle/Docs/jingle_changelog.txt new file mode 100644 index 0000000..6c4166c --- /dev/null +++ b/Plugins/jingle/Docs/jingle_changelog.txt @@ -0,0 +1,6 @@ +Jingle
+
+Changelog:
+
+. 0.0.0.1
+ + Initial version
\ No newline at end of file diff --git a/Plugins/jingle/Docs/jingle_readme.txt b/Plugins/jingle/Docs/jingle_readme.txt new file mode 100644 index 0000000..edb5a6e --- /dev/null +++ b/Plugins/jingle/Docs/jingle_readme.txt @@ -0,0 +1,35 @@ +Jingle - Jabber plugin
+---------------------
+
+CAUTION: THIS IS AN ALPHA STAGE PLUGIN. IT CAN DO VERY BAD THINGS. USE AT YOUR OWN RISK.
+
+This is a plugin for the jabber protocol, to allow it to make voice calls using libjingle. This is a very alpha version, that was tested very little and probabilly contains lots of bugs and TODOs.
+
+It can talk to Google Talk client and other jabber clients that implements the jingle spec.
+
+To make this work you need this mod of jabber that support plugins:
+Ansi: http://pescuma.mirandaim.ru/miranda/jabber_mp.zip
+Unicode: http://pescuma.mirandaim.ru/miranda/jabber_mpW.zip
+With some lucky it will be incorporated in the SVN soon.
+
+This plugin depends on Voice Service, that can be downloaded here:
+http://pescuma.mirandaim.ru/miranda/voiceservice
+Ansi: http://pescuma.mirandaim.ru/miranda/voiceservice.zip
+Unicode: http://pescuma.mirandaim.ru/miranda/voiceserviceW.zip
+
+Also, you need to copy the contents of the following zip to the same folder of miranda.exe:
+http://pescuma.mirandaim.ru/miranda/mediastreamer2.zip
+
+This plugin needs Miranda 0.7 to work.
+
+This plugin uses libjingle, code from linphone and a lot of other codecs. Thanks a lot to all the developers of these softwares.
+
+Todo:
+- Everything
+- A little bit more
+
+Known problems:
+- When making a call, takes a few seconds until both parts can hear each other
+- It does not take very well going offline while talking (aka crashes)
+
+To report bugs/make suggestions, go to the forum thread: http://forums.miranda-im.org/showthread.php?p=113763
diff --git a/Plugins/jingle/Docs/jingle_version.txt b/Plugins/jingle/Docs/jingle_version.txt new file mode 100644 index 0000000..c1c6199 --- /dev/null +++ b/Plugins/jingle/Docs/jingle_version.txt @@ -0,0 +1 @@ +Jingle 0.0.0.1
\ No newline at end of file diff --git a/Plugins/jingle/ZIP/doit.bat b/Plugins/jingle/ZIP/doit.bat new file mode 100644 index 0000000..a3137a9 --- /dev/null +++ b/Plugins/jingle/ZIP/doit.bat @@ -0,0 +1,54 @@ +@echo off
+
+rem Batch file to build and upload files
+rem
+rem TODO: Integration with FL
+
+set name=jingle
+
+rem To upload, this var must be set here or in other batch
+rem set ftp=ftp://<user>:<password>@<ftp>/<path>
+
+echo Building %name% ...
+
+"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.com" /Rebuild Release ..\%name%.sln
+"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.com" /Rebuild "Unicode Release" ..\%name%.sln
+
+echo Generating files for %name% ...
+
+del *.zip
+del *.dll
+copy ..\..\..\bin\release\Plugins\%name%.dll
+copy ..\..\..\bin\release\Plugins\%name%W.dll
+copy ..\Docs\%name%_changelog.txt
+copy ..\Docs\%name%_version.txt
+copy ..\Docs\%name%_readme.txt
+mkdir Docs
+cd Docs
+del /Q *.*
+copy ..\..\Docs\%name%_readme.txt
+cd ..
+
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%.zip %name%.dll Docs
+"C:\Program Files\Filzip\Filzip.exe" -a -rp %name%W.zip %name%W.dll Docs
+
+del *.dll
+cd Docs
+del /Q *.*
+cd ..
+rmdir Docs
+
+if "%ftp%"=="" GOTO END
+
+echo Going to upload files...
+pause
+
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%W.zip %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_changelog.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_version.txt %ftp% -overwrite -close
+"C:\Program Files\FileZilla\FileZilla.exe" -u .\%name%_readme.txt %ftp% -overwrite -close
+
+:END
+
+echo Done.
diff --git a/Plugins/jingle/commons.h b/Plugins/jingle/commons.h new file mode 100644 index 0000000..58b604a --- /dev/null +++ b/Plugins/jingle/commons.h @@ -0,0 +1,147 @@ +/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/client/httpportallocator.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/jid.h" +#include "talk/base/helpers.h" +#include "talk/base/thread.h" +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/session/phone/phonesessionclient.h" +
+#include <map> +#include <string> +#include <vector> +
+using namespace buzz;
+
+
+#include <windows.h>
+#include <time.h>
+
+
+// Miranda headers
+#define MIRANDA_VER 0x0700
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_updater.h>
+#include <m_popup.h>
+#include <m_message.h>
+#include "../../protocols/JabberG/m_jabber_plugin.h"
+#include "sdk/m_voice.h"
+#include "sdk/m_voiceservice.h"
+
+
+#include "../utils/mir_memory.h"
+#include "../utils/mir_options.h"
+
+
+// Global Variables
+extern HINSTANCE hInst;
+extern PLUGINLINK *pluginLink;
+
+class DATA;
+
+class SessionManagerTask : public sigslot::has_slots<> {
+ DATA *data;
+
+public:
+
+ SessionManagerTask(DATA *in) : data(in) {}
+ void OnOutgoingMessage(const XmlElement* stanza);
+};
+
+class CallClient;
+
+struct PendingIq {
+ time_t sent;
+
+ void (*callback)(DATA *data, void *param, IXmlNode *node);
+ void *param;
+};
+
+typedef std::map<int, PendingIq> PendingIqMap; +
+class DATA
+{
+public:
+
+ JABBER_DATA *jabber;
+ TCHAR fullJID[512];
+
+ HANDLE hVoiceNotify;
+
+ talk_base::NetworkManager *network_manager_;
+ talk_base::Thread *worker_thread_;
+ talk_base::Thread *signaling_thread_;
+ cricket::HttpPortAllocator *port_allocator_;
+ SessionManagerTask *session_manager_task_;
+ cricket::SessionManager *session_manager_;
+ CallClient *call_client_;
+ buzz::XmlnsStack xmlnsstack_;
+
+ CRITICAL_SECTION csPendingIqMap;
+ PendingIqMap pendingIqs;
+
+ bool started;
+};
+
+#include "libjingle_callclient.h" +
+
+
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+
+#define MIID_JINGLE { 0x9b9f6b3e, 0xd167, 0x4682, { 0x9b, 0x80, 0xef, 0xd0, 0x54, 0xa4, 0x85, 0xb1 } }
+
+
+#define JABBER_CAPS_MIRANDA_NODE "http://miranda-im.org/caps"
+#define JABBER_CAPS_GOOGLETALK_NODE "http://www.google.com/xmpp/client/caps"
+
+#define JABBER_EXT_JINGLE_VOICE "voice-v1"
+#define JABBER_EXT_JINGLE_SHARE "share-v1"
+
+#define JABBER_FEAT_JINGLE_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
+#define JABBER_CAPS_JINGLE_VOICE ((JabberCapsBits)1<<33)
+#define JABBER_FEAT_JINGLE_SHARE "http://www.google.com/xmpp/protocol/share/v1"
+#define JABBER_CAPS_JINGLE_SHARE ((JabberCapsBits)1<<34)
+
+
+std::string ToString(const TCHAR *str);
+
+#endif // __COMMONS_H__
diff --git a/Plugins/jingle/jingle.cpp b/Plugins/jingle/jingle.cpp new file mode 100644 index 0000000..58606bf --- /dev/null +++ b/Plugins/jingle/jingle.cpp @@ -0,0 +1,869 @@ +/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this file; see the file license.txt. If
+ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "commons.h"
+
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+#ifdef UNICODE
+ "Jingle (Unicode)",
+#else
+ "Jingle",
+#endif
+ PLUGIN_MAKE_VERSION(0,0,0,1),
+ "Jingle support for Jabber protocol",
+ "Ricardo Pescuma Domenecci",
+ "",
+ "© 2007 Ricardo Pescuma Domenecci",
+ "http://pescuma.mirandaim.ru/miranda/jingle",
+ UNICODE_AWARE,
+ 0, //doesn't replace anything built-in
+#if defined( _UNICODE )
+ { 0xf34e6d64, 0xc12e, 0x4621, { 0x98, 0x87, 0xbe, 0xa2, 0x42, 0xed, 0xea, 0x8a } } // {F34E6D64-C12E-4621-9887-BEA242EDEA8A}
+#else
+ { 0xaee557dd, 0xbc7b, 0x46d9, { 0xad, 0xcc, 0x82, 0x2a, 0x3f, 0xa3, 0xa, 0x90 } } // {AEE557DD-BC7B-46d9-ADCC-822A3FA30A90}
+#endif
+};
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+LIST_INTERFACE li;
+
+HANDLE hHooks[2] = {0};
+
+LIST<DATA> jabbers(2);
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+
+void RegisterJabberPlugin(const char *proto);
+__inline static int ProtoServiceExists(const char *szModule,const char *szService);
+
+static XmlElement *ConvertToXmlElement(DATA *data, IXmlNode *jabberNode);
+
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFO);
+ return (PLUGININFO*) &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ pluginInfo.cbSize = sizeof(PLUGININFOEX);
+ return &pluginInfo;
+}
+
+
+static const MUUID interfaces[] = { MIID_JINGLE, MIID_LAST };
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
+{
+ pluginLink = link;
+
+ init_mir_malloc();
+
+ li.cbSize = sizeof(li);
+ CallService(MS_SYSTEM_GET_LI, 0, (LPARAM) &li);
+
+ // hooks
+ hHooks[0] = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ for (int i = 0; i < jabbers.getCount(); i++)
+ {
+ DATA *data = jabbers[i];
+
+ if (data->hVoiceNotify != NULL)
+ DestroyHookableEvent(data->hVoiceNotify);
+
+ if (data->call_client_ != NULL)
+ delete data->call_client_;
+ if (data->network_manager_ != NULL)
+ delete data->network_manager_;
+ if (data->port_allocator_ != NULL)
+ delete data->port_allocator_;
+ if (data->session_manager_ != NULL)
+ delete data->session_manager_;
+ if (data->session_manager_task_ != NULL)
+ delete data->session_manager_task_;
+ if (data->signaling_thread_ != NULL)
+ delete data->signaling_thread_;
+ if (data->worker_thread_ != NULL)
+ delete data->worker_thread_;
+
+ DeleteCriticalSection(&data->csPendingIqMap);
+
+ free(data);
+ }
+ return 0;
+}
+
+// Called when all the modules are loaded
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ // updater plugin support
+ if(ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update upd = {0};
+ char szCurrentVersion[30];
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+
+ upd.szUpdateURL = UPDATER_AUTOREGISTER;
+
+ upd.szBetaVersionURL = "http://pescuma.mirandaim.ru/miranda/jingle_version.txt";
+ upd.szBetaChangelogURL = "http://pescuma.mirandaim.ru/miranda/jingle#Changelog";
+ upd.pbBetaVersionPrefix = (BYTE *)"Jingle ";
+ upd.cpbBetaVersionPrefix = strlen((char *)upd.pbBetaVersionPrefix);
+#ifdef UNICODE
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/jingleW.zip";
+#else
+ upd.szBetaUpdateURL = "http://pescuma.mirandaim.ru/miranda/jingle.zip";
+#endif
+
+ upd.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO*) &pluginInfo, szCurrentVersion);
+ upd.cpbVersion = strlen((char *)upd.pbVersion);
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+ }
+
+
+ if (!ServiceExists(MS_VOICESERVICE_REGISTER))
+ {
+ MessageBox(NULL, _T("Jingle needs Voice Service plugin to work."), _T("Jingle - Jabber plugin"), MB_OK | MB_ICONERROR);
+ return 0;
+ }
+
+ PROTOCOLDESCRIPTOR **protos;
+ int count;
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&count, (LPARAM)&protos);
+ for (int i = 0; i < count; i++)
+ {
+ if (protos[i]->type != PROTOTYPE_PROTOCOL)
+ continue;
+
+ if (protos[i]->szName == NULL || protos[i]->szName[0] == '\0')
+ continue;
+
+ // Found a protocol
+ RegisterJabberPlugin(protos[i]->szName);
+ }
+
+ return 0;
+}
+
+
+int PreShutdown(WPARAM wParam, LPARAM lParam)
+{
+ for (int i = 0; i < MAX_REGS(hHooks); ++i)
+ if (hHooks[i] != NULL)
+ UnhookEvent(hHooks[i]);
+
+ for (int i = 0; i < jabbers.getCount(); i++)
+ {
+ DATA *data = jabbers[i];
+
+ if (data->hVoiceNotify != NULL)
+ DestroyHookableEvent(data->hVoiceNotify);
+ }
+
+ return 0;
+}
+
+
+static void replace(std::string &str, char *find, char *replace)
+{
+ int pos = str.find(find);
+ if (pos >= 0)
+ str.replace(pos, strlen(find), replace);
+}
+
+
+void HandleStanzaReturn(DATA *data, void *param, IXmlNode *jabberNode)
+{
+ XmlElement *newStanza = ConvertToXmlElement(data, jabberNode);
+
+// OutputDebugStringA("<IN>\n\t");
+// OutputDebugStringA(newStanza->Str().c_str());
+// OutputDebugStringA("\n</IN>\n");
+
+ ReplyMessageData *rmd = new ReplyMessageData();
+ rmd->oldStanza = (buzz::XmlElement *) param;
+ rmd->newStanza = newStanza;
+ data->signaling_thread_->Post(data->call_client_, MSG_REPLY_MSG, rmd);
+}
+
+
+void SessionManagerTask ::OnOutgoingMessage(const XmlElement* stanza) {
+ if (!data->started)
+ return;
+
+ if (stanza->Attr(buzz::QN_TYPE) == "set")
+ {
+ buzz::XmlElement *newOne = new buzz::XmlElement(*stanza);
+
+ int iqId;
+ if (!newOne->HasAttr(buzz::QN_ID))
+ {
+ char tmp[100];
+ iqId = data->jabber->pfIqSerialNext();
+ mir_snprintf(tmp, MAX_REGS(tmp), "mir_%d", iqId);
+ newOne->SetAttr(buzz::QN_ID, std::string(tmp));
+ }
+ else
+ {
+ iqId = atoi(newOne->Attr(buzz::QN_ID).c_str());
+ }
+
+ EnterCriticalSection(&data->csPendingIqMap);
+ PendingIq &piq = data->pendingIqs[iqId];
+ piq.sent = time(NULL);
+ piq.callback = HandleStanzaReturn;
+ piq.param = newOne;
+ LeaveCriticalSection(&data->csPendingIqMap);
+
+ std::string str = newOne->Str();
+
+// OutputDebugStringA("<OUT>\n\t");
+// OutputDebugStringA(str.c_str());
+// OutputDebugStringA("\n</OUT>\n");
+
+ data->jabber->pfSendString(str.c_str());
+ }
+ else
+ {
+ std::string str = stanza->Str();
+
+// OutputDebugStringA("<OUT>\n\t");
+// OutputDebugStringA(str.c_str());
+// OutputDebugStringA("\n</OUT>\n");
+
+ data->jabber->pfSendString(str.c_str());
+ }
+}
+
+
+static bool StartsWithXmlns(const char *name) {
+ return name[0] == 'x' &&
+ name[1] == 'm' &&
+ name[2] == 'l' &&
+ name[3] == 'n' &&
+ name[4] == 's';
+}
+
+static QName ResolveQName(buzz::XmlnsStack &xmlnsstack_, const char *qname, bool isAttr) {
+ const char *c;
+ for (c = qname; *c; ++c) {
+ if (*c == ':') {
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname));
+ if (result == NULL)
+ return QN_EMPTY;
+ const char * localname = c + 1;
+ return QName(*result, localname);
+ }
+ }
+ if (isAttr) {
+ return QName(STR_EMPTY, qname);
+ }
+
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(STR_EMPTY);
+ if (result == NULL)
+ return QN_EMPTY;
+
+ return QName(*result, qname);
+}
+
+
+std::string ToString(const TCHAR *str)
+{
+ if (str == NULL)
+ return std::string();
+
+#ifdef UNICODE
+
+ size_t size = lstrlenW(str) + 1;
+ char *tmp = (char *) mir_alloc(size);
+
+ WideCharToMultiByte(CP_UTF8, 0, str, -1, tmp, size, NULL, NULL);
+
+ std::string ret(tmp);
+
+ mir_free(tmp);
+
+ return ret;
+
+#else
+
+ return std::string(str);
+
+#endif
+}
+
+
+static XmlElement *ConvertToXmlElement(DATA *data, buzz::XmlnsStack &xmlnsstack_, IXmlNode *jabberNode)
+{
+ xmlnsstack_.PushFrame();
+
+ for (int i=0; i < data->jabber->pfXmlGetNumAttr(jabberNode); i++)
+ {
+ const char *name = data->jabber->pfXmlGetAttrName(jabberNode, i);
+ if (StartsWithXmlns(name))
+ {
+ const TCHAR *value = data->jabber->pfXmlGetAttrValue(jabberNode, i);
+ if (name[5] == '\0')
+ {
+ xmlnsstack_.AddXmlns(STR_EMPTY, ToString(value));
+ }
+ else if (name[5] == ':')
+ {
+ xmlnsstack_.AddXmlns(std::string(name + 6), ToString(value));
+ }
+ }
+ }
+
+ QName tagName(ResolveQName(xmlnsstack_, data->jabber->pfXmlGetNodeName(jabberNode), false));
+ if (tagName == QN_EMPTY)
+ {
+ xmlnsstack_.PopFrame();
+ return NULL;
+ }
+
+ XmlElement * pelNew = new XmlElement(tagName);
+
+ for ( int i=0; i < data->jabber->pfXmlGetNumAttr(jabberNode); i++ )
+ {
+ QName attName(ResolveQName(xmlnsstack_, data->jabber->pfXmlGetAttrName(jabberNode, i), true));
+ if (attName == QN_EMPTY)
+ continue;
+
+ pelNew->AddAttr(attName, ToString(data->jabber->pfXmlGetAttrValue(jabberNode, i)));
+ }
+
+ const TCHAR *text = data->jabber->pfXmlGetNodeText(jabberNode);
+ if (text != NULL)
+ pelNew->AddText(ToString(text));
+
+ for ( int i=0; i < data->jabber->pfXmlGetNumChild(jabberNode); i++ )
+ {
+ XmlElement *tmp = ConvertToXmlElement(data, data->jabber->pfXmlGetChild(jabberNode, i));
+ if (tmp != NULL)
+ pelNew->AddElement(tmp);
+ }
+
+ xmlnsstack_.PopFrame();
+ return pelNew;
+}
+
+// Convert an XML element from our representation to libjingle's one
+static XmlElement *ConvertToXmlElement(DATA *data, IXmlNode *jabberNode)
+{
+ return ConvertToXmlElement(data, data->xmlnsstack_, jabberNode);
+}
+
+static int VoiceGetInfo(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return 0;
+
+ return VOICE_SUPPORTED | VOICE_CALL_CONTACT | VOICE_CALL_CONTACT_NEED_TEST;
+}
+
+static int ContactValidForVoice(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (lParam == FALSE)
+ return TRUE;
+
+ HANDLE hContact = (HANDLE) wParam;
+ if (hContact == NULL)
+ return FALSE;
+
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, data->jabber->protocolName, "jid", &dbv))
+ return FALSE;
+
+ TCHAR jid[512];
+ _tcsncpy(jid, dbv.ptszVal, MAX_REGS(jid));
+ DBFreeVariant(&dbv);
+
+ if (DBGetContactSettingWord(hContact, data->jabber->protocolName, "Status", ID_STATUS_OFFLINE) <= ID_STATUS_OFFLINE)
+ return FALSE;
+
+ return (data->jabber->pfGetResourceCapabilites(jid) & JABBER_CAPS_JINGLE_VOICE) == JABBER_CAPS_JINGLE_VOICE;
+}
+
+int VoiceCall(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->MakeCallTo((HANDLE) wParam);
+ return 0;
+}
+
+static int VoiceAnswer(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->AnswerCall(atoi((const char *) wParam));
+ return 0;
+}
+
+static int VoiceDrop(WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ if (wParam == NULL)
+ return -1;
+
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (data->call_client_ != NULL)
+ data->call_client_->DropCall(atoi((const char *) wParam));
+ return 0;
+}
+
+
+VOID CALLBACK ExpireMessagesTimer(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ DATA *data = jabbers[(int) idEvent];
+ if (data == NULL)
+ return;
+
+ data->signaling_thread_->Post(data->call_client_, MSG_TIMER);
+}
+
+
+void AssertLibjingleStarted(int id, DATA *data)
+{
+ if (data->started)
+ return;
+
+ // Libjingle stuff
+ data->network_manager_ = new talk_base::NetworkManager();
+ data->worker_thread_ = new talk_base::Thread();
+ data->signaling_thread_ = new talk_base::Thread();
+ data->port_allocator_ = new cricket::HttpPortAllocator(data->network_manager_, "call");
+ data->session_manager_task_ = new SessionManagerTask(data);
+ data->session_manager_ = new cricket::SessionManager(data->port_allocator_, data->worker_thread_, data->signaling_thread_);
+ data->session_manager_->SignalOutgoingMessage.connect(data->session_manager_task_, &SessionManagerTask::OnOutgoingMessage);
+
+ data->worker_thread_->Start();
+ data->signaling_thread_->Start();
+
+ //talk_base::ThreadManager::SetCurrent(signaling_thread_);
+
+ data->call_client_ = new CallClient(data);
+ data->session_manager_->SignalRequestSignaling.connect(data->call_client_, &CallClient::OnSignalingReady);
+
+ SetTimer(NULL, id, 1000, ExpireMessagesTimer);
+
+ data->started = true;
+}
+
+
+BOOL HandleIqReturn(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ //TCHAR *from = data->jabber->pfXmlGetAttrValueStr(node, "from");
+ //if (from == NULL)
+ // return FALSE;
+
+ if (lstrlen(id) < 5)
+ return FALSE;
+
+ int iqId = _ttoi(&id[4]);
+
+ EnterCriticalSection(&data->csPendingIqMap);
+
+ PendingIqMap::iterator it = data->pendingIqs.find(iqId);
+ if (it == data->pendingIqs.end())
+ {
+ LeaveCriticalSection(&data->csPendingIqMap);
+ return FALSE;
+ }
+
+ //XmlElement *oldStanza = it->second.stanza;
+ //if (oldStanza->Attr(QN_TO) != ToString(from))
+ //{
+ // LeaveCriticalSection( &csPendingIqMap );
+ // return FALSE;
+ //}
+
+ PendingIq iq = it->second;
+ data->pendingIqs.erase(it);
+
+ LeaveCriticalSection(&data->csPendingIqMap);
+
+ iq.callback(data, iq.param, node);
+ return TRUE;
+}
+
+
+BOOL HandleSet(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ IXmlNode *sessionNode = data->jabber->pfXmlGetChildWithGivenAttrValue(node, "session", "xmlns", _T("http://www.google.com/session"));
+ if (sessionNode == NULL)
+ return FALSE;
+
+ data->xmlnsstack_.Reset();
+ XmlElement *el = ConvertToXmlElement(data, node);
+ if (el == NULL)
+ return FALSE;
+
+// OutputDebugStringA("<IN>\n\t");
+// OutputDebugStringA(el->Str().c_str());
+// OutputDebugStringA("\n</IN>\n");
+
+ data->signaling_thread_->Post(data->call_client_, MSG_INCOMING_MSG, (talk_base::MessageData *) el);
+ return TRUE;
+}
+
+
+BOOL HandleGet(DATA *data, IXmlNode *node, const TCHAR *id)
+{
+ IXmlNode *query = data->jabber->pfXmlGetChildWithGivenAttrValue(node, "query", "xmlns", _T("http://jabber.org/protocol/disco#info"));
+ if (query == NULL)
+ return FALSE;
+
+ const TCHAR *feat = data->jabber->pfXmlGetAttrValueStr(query, "node");
+ if (feat == NULL)
+ return FALSE;
+
+ if (lstrcmp(feat, _T(JABBER_CAPS_MIRANDA_NODE) _T("#") _T(JABBER_EXT_JINGLE_VOICE)) != 0)
+ return FALSE;
+
+ const TCHAR *from = data->jabber->pfXmlGetAttrValueStr(node, "from");
+ if (from == NULL)
+ return FALSE;
+
+ IXmlNode *iq = data->jabber->pfXmlCreateNode("iq");
+ data->jabber->pfXmlAddAttr(iq, "type", _T("result"));
+ data->jabber->pfXmlAddAttr(iq, "to", from);
+ data->jabber->pfXmlAddAttr(iq, "id", id);
+
+ IXmlNode *q = data->jabber->pfXmlAddChild(iq, "query");
+ data->jabber->pfXmlAddAttr(q, "xmlns", _T("http://jabber.org/protocol/disco#info"));
+
+ char *ver = data->jabber->pfGetVersionText();
+ TCHAR capsVer[512];
+ mir_sntprintf(capsVer, MAX_REGS(capsVer), _T(JABBER_CAPS_MIRANDA_NODE) _T("#") _T(TCHAR_STR_PARAM), ver);
+ data->jabber->pfXmlAddAttr(q, "node", capsVer);
+
+ IXmlNode *f = data->jabber->pfXmlAddChild(q, "feature");
+ data->jabber->pfXmlAddAttr(f, "var", _T(JABBER_FEAT_JINGLE_VOICE));
+
+ data->jabber->pfSendNode(iq);
+
+ mir_free(ver);
+ return TRUE;
+}
+
+
+BOOL OnXmlReceived(void *param, IXmlNode *node)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return FALSE;
+
+ if (!data->started)
+ return FALSE;
+
+ const char *name = data->jabber->pfXmlGetNodeName(node);
+ if (name == NULL || strcmp(name, "iq") != 0)
+ return FALSE;
+
+ const TCHAR *type = data->jabber->pfXmlGetAttrValueStr(node, "type");
+ if (type == NULL)
+ return FALSE;
+
+ const TCHAR * id = data->jabber->pfXmlGetAttrValueStr(node, "id");
+ if (id == NULL)
+ return FALSE;
+
+ if (_tcscmp(type, _T("result")) == 0 || _tcscmp(type, _T("error")) == 0)
+ return HandleIqReturn(data, node, id);
+
+ else if (_tcscmp(type, _T("set")) == 0)
+ return HandleSet(data, node, id);
+
+ else if (_tcscmp(type, _T("get")) == 0)
+ return HandleGet(data, node, id);
+
+ else
+ return FALSE;
+}
+
+void JingleInfoResult(DATA *data, void *param, IXmlNode *iqNode)
+{
+ const TCHAR *type = data->jabber->pfXmlGetAttrValueStr(iqNode, "type");
+ if (_tcscmp( type, _T("error")) == 0)
+ return;
+
+ IXmlNode *query = data->jabber->pfXmlGetChildWithGivenAttrValue(iqNode, "query", "xmlns", _T("google:jingleinfo"));
+ if (query == NULL)
+ return;
+
+ std::vector<std::string> relay_hosts; + std::vector<talk_base::SocketAddress> stun_hosts; + std::string relay_token; + + IXmlNode *stun = data->jabber->pfXmlGetChildByName(query, "stun");
+ if (stun != NULL)
+ {
+ for (int i = 0; i < data->jabber->pfXmlGetNumChild(stun); i++)
+ {
+ IXmlNode *child = data->jabber->pfXmlGetChild(stun, i);
+ const char *name = data->jabber->pfXmlGetNodeName(child);
+ if (name != NULL && !strcmp("server", name))
+ {
+ std::string host = ToString(data->jabber->pfXmlGetAttrValueStr(child, "host")); + std::string port = ToString(data->jabber->pfXmlGetAttrValueStr(child, "udp")); + if (host.length() > 0 && port.length() > 0) + stun_hosts.push_back(talk_base::SocketAddress(host, atoi(port.c_str()))); + }
+ }
+ }
+
+ IXmlNode *relay = data->jabber->pfXmlGetChildByName(query, "relay");
+ if (relay != NULL)
+ {
+ for (int i = 0; i < data->jabber->pfXmlGetNumChild(relay); i++)
+ {
+ IXmlNode *child = data->jabber->pfXmlGetChild(relay, i);
+ const char *name = data->jabber->pfXmlGetNodeName(child);
+
+ if (name == NULL)
+ continue;
+
+ if (strcmp("server", name) == 0)
+ {
+ std::string host = ToString(data->jabber->pfXmlGetAttrValueStr(child, "host")); + if (host.length() > 0) + relay_hosts.push_back(host); + }
+ else if (strcmp("token", name) == 0)
+ {
+ const TCHAR *text = data->jabber->pfXmlGetNodeText(child);
+ if (text != NULL && text[0] != '\0')
+ relay_token = ToString(text);
+ }
+ }
+ }
+ + data->port_allocator_->SetStunHosts(stun_hosts); + data->port_allocator_->SetRelayHosts(relay_hosts); + data->port_allocator_->SetRelayToken(relay_token); +}
+
+
+void OnConnect(void *param, TCHAR *fullJID)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ AssertLibjingleStarted((int) param, data);
+
+ _tcsncpy(data->fullJID, fullJID, MAX_REGS(data->fullJID));
+ data->call_client_->OnConnect();
+
+ int iqId = data->jabber->pfIqSerialNext();
+
+ IXmlNode *iq = data->jabber->pfXmlCreateNode("iq");
+ data->jabber->pfXmlAddAttr(iq, "type", _T("get"));
+ data->jabber->pfXmlAddAttrID(iq, iqId);
+ IXmlNode *query = data->jabber->pfXmlAddChild(iq, "query");
+ data->jabber->pfXmlAddAttr(query, "xmlns", _T("google:jingleinfo"));
+
+ data->jabber->pfSendNode(iq);
+ data->jabber->pfXmlDeleteNode(iq);
+
+ EnterCriticalSection(&data->csPendingIqMap);
+ PendingIq &piq = data->pendingIqs[iqId];
+ piq.sent = time(NULL);
+ piq.callback = JingleInfoResult;
+ piq.param = 0;
+ LeaveCriticalSection(&data->csPendingIqMap);
+}
+
+void OnDisconnect(void *param)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return;
+
+ if (!data->started)
+ return;
+
+ data->call_client_->OnDisconnect();
+ data->signaling_thread_->Post(data->call_client_, TERMINATE_ALL, NULL);
+}
+
+BOOL OnXmlSendNode(void *param, IXmlNode *node)
+{
+ DATA *data = jabbers[(int) param];
+ if (data == NULL)
+ return TRUE;
+
+ const char *name = data->jabber->pfXmlGetNodeName(node);
+ if (name == NULL || strcmp(name, "presence") != 0)
+ return TRUE;
+
+ IXmlNode *c = data->jabber->pfXmlGetChildByName(node, "c");
+ if (c == NULL)
+ return TRUE;
+
+ const TCHAR *ext = data->jabber->pfXmlGetAttrValueStr(c, "ext");
+ if (ext == NULL)
+ data->jabber->pfXmlAddAttr(c, "ext", _T(JABBER_EXT_JINGLE_VOICE));
+ else
+ {
+ TCHAR tmp[512];
+ mir_sntprintf(tmp, MAX_REGS(tmp), _T("%s %s"), ext, _T(JABBER_EXT_JINGLE_VOICE));
+ data->jabber->pfXmlSetAttrValueStr(c, "ext", tmp);
+ }
+
+ return TRUE;
+}
+
+
+void RegisterJabberPlugin(const char *proto)
+{
+ if (!ProtoServiceExists(proto, PS_REGISTER_JABBER_PLUGIN))
+ return;
+
+ int id = jabbers.getCount();
+ JABBER_PLUGIN_DATA info = {
+ sizeof(JABBER_PLUGIN_DATA),
+ "Jingle",
+ "Enable voice calls",
+ TRUE,
+ (void *) id,
+ OnConnect,
+ OnDisconnect,
+ OnXmlReceived,
+ NULL,
+ OnXmlSendNode,
+ NULL,
+ NULL
+ };
+
+
+ JABBER_DATA *jabber = (JABBER_DATA *) CallProtoService(proto, PS_REGISTER_JABBER_PLUGIN, (WPARAM) &info, 1);
+ if (jabber == NULL)
+ // We are disabled / ignored
+ return;
+
+ DATA *data = new DATA();
+ data->network_manager_ = NULL;
+ data->worker_thread_ = NULL;
+ data->signaling_thread_ = NULL;
+ data->port_allocator_ = NULL;
+ data->session_manager_task_ = NULL;
+ data->session_manager_ = NULL;
+ data->call_client_ = NULL;
+ data->started = false;
+
+ data->jabber = jabber;
+
+ jabbers.insert(data, jabbers.getCount());
+
+ InitializeCriticalSection(&data->csPendingIqMap);
+
+ // Voice service support
+ char tmp[256];
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PE_VOICE_CALL_STATE, data->jabber->protocolName);
+ data->hVoiceNotify = CreateHookableEvent(tmp);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_GETINFO, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceGetInfo, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_CALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceCall, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_ANSWERCALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceAnswer, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_DROPCALL, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, VoiceDrop, id);
+
+ mir_snprintf(tmp, MAX_REGS(tmp), "%s" PS_VOICE_CALL_CONTACT_VALID, data->jabber->protocolName);
+ CreateServiceFunctionParam(tmp, ContactValidForVoice, id);
+
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_MIRANDA_NODE), _T(JABBER_EXT_JINGLE_VOICE), JABBER_CAPS_JINGLE_VOICE);
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_GOOGLETALK_NODE), _T(JABBER_EXT_JINGLE_VOICE), JABBER_CAPS_JINGLE_VOICE);
+ data->jabber->pfSetClientCaps(_T(JABBER_CAPS_GOOGLETALK_NODE), _T(JABBER_EXT_JINGLE_SHARE), JABBER_CAPS_JINGLE_SHARE);
+}
+
+
+__inline static int ProtoServiceExists(const char *szModule,const char *szService)
+{
+ char str[MAXMODULELABELLENGTH];
+ strcpy(str,szModule);
+ strcat(str,szService);
+ return ServiceExists(str);
+}
diff --git a/Plugins/jingle/jingle.sln b/Plugins/jingle/jingle.sln new file mode 100644 index 0000000..fae28e3 --- /dev/null +++ b/Plugins/jingle/jingle.sln @@ -0,0 +1,39 @@ +
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jingle", "jingle.vcproj", "{F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjingle", "libjingle\talk\libjingle.vcproj", "{DC948D76-8503-490C-A07D-11044004FCE3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Unicode Debug|Win32 = Unicode Debug|Win32
+ Unicode Release|Win32 = Unicode Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Debug|Win32.Build.0 = Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Release|Win32.ActiveCfg = Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Release|Win32.Build.0 = Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Debug|Win32.ActiveCfg = Unicode Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Debug|Win32.Build.0 = Unicode Debug|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Release|Win32.ActiveCfg = Unicode Release|Win32
+ {F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}.Unicode Release|Win32.Build.0 = Unicode Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.Build.0 = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Unicode Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/jingle/jingle.vcproj b/Plugins/jingle/jingle.vcproj new file mode 100644 index 0000000..ec26f47 --- /dev/null +++ b/Plugins/jingle/jingle.vcproj @@ -0,0 +1,600 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="jingle"
+ ProjectGUID="{F21816C0-06BA-4FAA-B9F2-FB90B2BDC2DB}"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Unicode Release|Win32"
+ OutputDirectory=".\Unicode_Release"
+ IntermediateDirectory=".\Unicode_Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Release/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Unicode_Release/jingle.pch"
+ AssemblerListingLocation=".\Unicode_Release/"
+ ObjectFile=".\Unicode_Release/"
+ ProgramDataBaseFileName=".\Unicode_Release/"
+ BrowseInformation="0"
+ BrowseInformationFile=".\Unicode_Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\jingleW.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Release/jingleW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Unicode_Release/jingleW.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Release/jingleW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Release/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="3"
+ PrecompiledHeaderFile=".\Debug/jingle.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\debug\Plugins\jingle.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/jingle.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Debug/jingle.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Unicode Debug|Win32"
+ OutputDirectory=".\Unicode_Debug"
+ IntermediateDirectory=".\Unicode_Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Unicode_Debug/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;_DEBUG;_WINDOWS;UNICODE;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ RuntimeLibrary="3"
+ PrecompiledHeaderFile=".\Unicode_Debug/jingle.pch"
+ AssemblerListingLocation=".\Unicode_Debug/"
+ ObjectFile=".\Unicode_Debug/"
+ ProgramDataBaseFileName=".\Unicode_Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\debug unicode\Plugins\jingleW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Unicode_Debug/jingleW.pdb"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Unicode_Debug/jingleW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Unicode_Debug/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/jingle.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="../../include,sdk,libjingle"
+ PreprocessorDefinitions="WIN32;W32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/jingle.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="2"
+ BrowseInformationFile=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1047"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /filealign:0x200 /ignore:4108 "
+ AdditionalDependencies="mediastreamer2.lib Ws2_32.lib Secur32.lib Crypt32.lib Iphlpapi.lib odbc32.lib odbccp32.lib wsock32.lib version.lib comctl32.lib"
+ OutputFile="..\..\bin\release\Plugins\jingle.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories="libjingle\talk\third_party\dll\"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Release/jingle.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Release/jingle.map"
+ BaseAddress="0x3EC20000"
+ ImportLibrary=".\Release/jingle.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/jingle.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="commons.h"
+ >
+ </File>
+ <File
+ RelativePath=".\libjingle_callclient.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="jingle.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\libjingle_callclient.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\utils\mir_memory.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\utils\mir_options.cpp"
+ >
+ <FileConfiguration
+ Name="Unicode Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Unicode Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Docs"
+ >
+ <File
+ RelativePath="Docs\jingle_changelog.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\jingle_readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\jingle_version.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\langpack_jingle.txt"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/COPYING b/Plugins/jingle/libjingle/COPYING new file mode 100644 index 0000000..d11f105 --- /dev/null +++ b/Plugins/jingle/libjingle/COPYING @@ -0,0 +1,25 @@ +Copyright (c) 2004--2005, Google 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + * The name of the author may not 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.
\ No newline at end of file diff --git a/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h b/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h new file mode 100644 index 0000000..6ff97a6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/Equifax_Secure_Global_eBusiness_CA-1.h @@ -0,0 +1,55 @@ +// This file is the Equifax Secure global eBusiness CA-1 certificate +// in C form. + +// It was generated with the following command line: +// > openssl x509 -in Equifax_Secure_Global_eBusiness_CA-1.cer -noout -C + +// The certificate was retrieved from: +// http://www.geotrust.com/resources/root_certificates/certificates/Equifax_Secure_Global_eBusiness_CA-1.cer + +/* subject:/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */ +/* issuer :/C=US/O=Equifax Secure Inc./CN=Equifax Secure Global eBusiness CA-1 */ +unsigned char EquifaxSecureGlobalEBusinessCA1_certificate[660]={ +0x30,0x82,0x02,0x90,0x30,0x82,0x01,0xF9,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01, +0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30, +0x5A,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C, +0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78, +0x20,0x53,0x65,0x63,0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B, +0x06,0x03,0x55,0x04,0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53, +0x65,0x63,0x75,0x72,0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75, +0x73,0x69,0x6E,0x65,0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x1E,0x17,0x0D,0x39, +0x39,0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,0x30, +0x30,0x36,0x32,0x31,0x30,0x34,0x30,0x30,0x30,0x30,0x5A,0x30,0x5A,0x31,0x0B,0x30, +0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x1C,0x30,0x1A,0x06,0x03, +0x55,0x04,0x0A,0x13,0x13,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63, +0x75,0x72,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x2D,0x30,0x2B,0x06,0x03,0x55,0x04, +0x03,0x13,0x24,0x45,0x71,0x75,0x69,0x66,0x61,0x78,0x20,0x53,0x65,0x63,0x75,0x72, +0x65,0x20,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x65,0x42,0x75,0x73,0x69,0x6E,0x65, +0x73,0x73,0x20,0x43,0x41,0x2D,0x31,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86, +0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89, +0x02,0x81,0x81,0x00,0xBA,0xE7,0x17,0x90,0x02,0x65,0xB1,0x34,0x55,0x3C,0x49,0xC2, +0x51,0xD5,0xDF,0xA7,0xD1,0x37,0x8F,0xD1,0xE7,0x81,0x73,0x41,0x52,0x60,0x9B,0x9D, +0xA1,0x17,0x26,0x78,0xAD,0xC7,0xB1,0xE8,0x26,0x94,0x32,0xB5,0xDE,0x33,0x8D,0x3A, +0x2F,0xDB,0xF2,0x9A,0x7A,0x5A,0x73,0x98,0xA3,0x5C,0xE9,0xFB,0x8A,0x73,0x1B,0x5C, +0xE7,0xC3,0xBF,0x80,0x6C,0xCD,0xA9,0xF4,0xD6,0x2B,0xC0,0xF7,0xF9,0x99,0xAA,0x63, +0xA2,0xB1,0x47,0x02,0x0F,0xD4,0xE4,0x51,0x3A,0x12,0x3C,0x6C,0x8A,0x5A,0x54,0x84, +0x70,0xDB,0xC1,0xC5,0x90,0xCF,0x72,0x45,0xCB,0xA8,0x59,0xC0,0xCD,0x33,0x9D,0x3F, +0xA3,0x96,0xEB,0x85,0x33,0x21,0x1C,0x3E,0x1E,0x3E,0x60,0x6E,0x76,0x9C,0x67,0x85, +0xC5,0xC8,0xC3,0x61,0x02,0x03,0x01,0x00,0x01,0xA3,0x66,0x30,0x64,0x30,0x11,0x06, +0x09,0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,0x04,0x04,0x03,0x02,0x00,0x07, +0x30,0x0F,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x05,0x30,0x03,0x01,0x01, +0xFF,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xBE,0xA8, +0xA0,0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B, +0x68,0x6C,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xBE,0xA8,0xA0, +0x74,0x72,0x50,0x6B,0x44,0xB7,0xC9,0x23,0xD8,0xFB,0xA8,0xFF,0xB3,0x57,0x6B,0x68, +0x6C,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00, +0x03,0x81,0x81,0x00,0x30,0xE2,0x01,0x51,0xAA,0xC7,0xEA,0x5F,0xDA,0xB9,0xD0,0x65, +0x0F,0x30,0xD6,0x3E,0xDA,0x0D,0x14,0x49,0x6E,0x91,0x93,0x27,0x14,0x31,0xEF,0xC4, +0xF7,0x2D,0x45,0xF8,0xEC,0xC7,0xBF,0xA2,0x41,0x0D,0x23,0xB4,0x92,0xF9,0x19,0x00, +0x67,0xBD,0x01,0xAF,0xCD,0xE0,0x71,0xFC,0x5A,0xCF,0x64,0xC4,0xE0,0x96,0x98,0xD0, +0xA3,0x40,0xE2,0x01,0x8A,0xEF,0x27,0x07,0xF1,0x65,0x01,0x8A,0x44,0x2D,0x06,0x65, +0x75,0x52,0xC0,0x86,0x10,0x20,0x21,0x5F,0x6C,0x6B,0x0F,0x6C,0xAE,0x09,0x1C,0xAF, +0xF2,0xA2,0x18,0x34,0xC4,0x75,0xA4,0x73,0x1C,0xF1,0x8D,0xDC,0xEF,0xAD,0xF9,0xB3, +0x76,0xB4,0x92,0xBF,0xDC,0x95,0x10,0x1E,0xBE,0xCB,0xC8,0x3B,0x5A,0x84,0x60,0x19, +0x56,0x94,0xA9,0x55, +}; diff --git a/Plugins/jingle/libjingle/talk/base/asyncfile.h b/Plugins/jingle/libjingle/talk/base/asyncfile.h new file mode 100644 index 0000000..1437979 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncfile.h @@ -0,0 +1,56 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_ASYNCFILE_H__ +#define TALK_BASE_ASYNCFILE_H__ + +#include "talk/base/sigslot.h" + +namespace talk_base { + +// Provides the ability to perform file I/O asynchronously. +// TODO: Create a common base class with AsyncSocket. +class AsyncFile { +public: + virtual ~AsyncFile() {} + + // Determines whether the file will receive read events. + virtual bool readable() = 0; + virtual void set_readable(bool value) = 0; + + // Determines whether the file will receive write events. + virtual bool writable() = 0; + virtual void set_writable(bool value) = 0; + + sigslot::signal1<AsyncFile*> SignalReadEvent; + sigslot::signal1<AsyncFile*> SignalWriteEvent; + sigslot::signal2<AsyncFile*,int> SignalCloseEvent; +}; + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCFILE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc b/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc new file mode 100644 index 0000000..bf7bc85 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asynchttprequest.cc @@ -0,0 +1,155 @@ +#include "talk/base/common.h" +#include "talk/base/firewallsocketserver.h" +#include "talk/base/httpclient.h" +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/socketadapters.h" +#include "talk/base/socketpool.h" +#include "talk/base/ssladapter.h" +#include "talk/base/asynchttprequest.h" + +using namespace talk_base; + +/////////////////////////////////////////////////////////////////////////////// +// HttpMonitor +/////////////////////////////////////////////////////////////////////////////// + +HttpMonitor::HttpMonitor(SocketServer *ss) { + ASSERT(talk_base::Thread::Current() != NULL); + ss_ = ss; + reset(); +} + +void HttpMonitor::Connect(talk_base::HttpClient *http) { + http->SignalHttpClientComplete.connect(this, + &HttpMonitor::OnHttpClientComplete); +} + +void HttpMonitor::OnHttpClientComplete(talk_base::HttpClient * http, int err) { + complete_ = true; + err_ = err; + ss_->WakeUp(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SslSocketFactory +/////////////////////////////////////////////////////////////////////////////// + +talk_base::Socket * SslSocketFactory::CreateSocket(int type) { + return factory_->CreateSocket(type); +} + +talk_base::AsyncSocket * SslSocketFactory::CreateAsyncSocket(int type) { + talk_base::AsyncSocket * socket = factory_->CreateAsyncSocket(type); + if (!socket) + return 0; + + // Binary logging happens at the lowest level + if (!logging_label_.empty() && binary_mode_) { + socket = new talk_base::LoggingSocketAdapter(socket, logging_level_, + logging_label_.c_str(), + binary_mode_); + } + + if (proxy_.type) { + talk_base::AsyncSocket * proxy_socket = 0; + if (proxy_.type == talk_base::PROXY_SOCKS5) { + proxy_socket = new talk_base::AsyncSocksProxySocket(socket, proxy_.address, + proxy_.username, proxy_.password); + } else { + // Note: we are trying unknown proxies as HTTPS currently + proxy_socket = new talk_base::AsyncHttpsProxySocket(socket, + agent_, proxy_.address, + proxy_.username, proxy_.password); + } + if (!proxy_socket) { + delete socket; + return 0; + } + socket = proxy_socket; // for our purposes the proxy is now the socket + } + + if (!hostname_.empty()) { + talk_base::SSLAdapter * ssl_adapter = talk_base::SSLAdapter::Create(socket); + ssl_adapter->set_ignore_bad_cert(ignore_bad_cert_); + ssl_adapter->StartSSL(hostname_.c_str(), true); + socket = ssl_adapter; + } + + // Regular logging occurs at the highest level + if (!logging_label_.empty() && !binary_mode_) { + socket = new talk_base::LoggingSocketAdapter(socket, logging_level_, + logging_label_.c_str(), + binary_mode_); + } + return socket; +} + +/////////////////////////////////////////////////////////////////////////////// +// AsyncHttpRequest +/////////////////////////////////////////////////////////////////////////////// + +const int kDefaultHTTPTimeout = 30 * 1000; // 30 sec + +AsyncHttpRequest::AsyncHttpRequest(const std::string &user_agent) +: firewall_(0), port_(80), secure_(false), + timeout_(kDefaultHTTPTimeout), fail_redirect_(false), + client_(user_agent.c_str(), NULL) +{ +} + +void AsyncHttpRequest::DoWork() { + // TODO: Rewrite this to use the thread's native socket server, and a more + // natural flow? + + talk_base::PhysicalSocketServer physical; + talk_base::SocketServer * ss = &physical; + if (firewall_) { + ss = new talk_base::FirewallSocketServer(ss, firewall_); + } + + SslSocketFactory factory(ss, client_.agent()); + factory.SetProxy(proxy_); + if (secure_) + factory.UseSSL(host_.c_str()); + + //factory.SetLogging("AsyncHttpRequest"); + + talk_base::ReuseSocketPool pool(&factory); + client_.set_pool(&pool); + + bool transparent_proxy = (port_ == 80) + && ((proxy_.type == talk_base::PROXY_HTTPS) + || (proxy_.type == talk_base::PROXY_UNKNOWN)); + + if (transparent_proxy) { + client_.set_proxy(proxy_); + } + client_.set_fail_redirect(fail_redirect_); + + talk_base::SocketAddress server(host_, port_); + client_.set_server(server); + + HttpMonitor monitor(ss); + monitor.Connect(&client_); + client_.start(); + ss->Wait(timeout_, true); + if (!monitor.done()) { + LOG(LS_INFO) << "AsyncHttpRequest request timed out"; + client_.reset(); + return; + } + + if (monitor.error()) { + LOG(LS_INFO) << "AsyncHttpRequest request error: " << monitor.error(); + if (monitor.error() == talk_base::HE_AUTH) { + //proxy_auth_required_ = true; + } + return; + } + + std::string value; + if (client_.response().hasHeader(HH_LOCATION, &value)) { + response_redirect_ = value.c_str(); + } +} diff --git a/Plugins/jingle/libjingle/talk/base/asynchttprequest.h b/Plugins/jingle/libjingle/talk/base/asynchttprequest.h new file mode 100644 index 0000000..65bfa26 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asynchttprequest.h @@ -0,0 +1,140 @@ +#ifndef _ASYNCHTTPREQUEST_H_ +#define _ASYNCHTTPREQUEST_H_ + +#include "talk/base/httpclient.h" +#include "talk/base/logging.h" +#include "talk/base/proxyinfo.h" +#include "talk/base/socketserver.h" +#include "talk/base/thread.h" +#include "talk/base/signalthread.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// AsyncHttpRequest +// Performs an HTTP request on a background thread. Notifies on the foreground +// thread once the request is done (successfully or unsuccessfully). +/////////////////////////////////////////////////////////////////////////////// + +class FirewallManager; +class MemoryStream; + +class AsyncHttpRequest: + public SignalThread, + public sigslot::has_slots<> { +public: + AsyncHttpRequest(const std::string &user_agent); + + void set_proxy(const talk_base::ProxyInfo& proxy) { + proxy_ = proxy; + } + void set_firewall(talk_base::FirewallManager * firewall) { + firewall_ = firewall; + } + + // The DNS name of the host to connect to. + const std::string& host() { return host_; } + void set_host(const std::string& host) { host_ = host; } + + // The port to connect to on the target host. + int port() { return port_; } + void set_port(int port) { port_ = port; } + + // Whether the request should use SSL. + bool secure() { return secure_; } + void set_secure(bool secure) { secure_ = secure; } + + // Returns the redirect when redirection occurs + const std::string& response_redirect() { return response_redirect_; } + + // Time to wait on the download, in ms. Default is 5000 (5s) + int timeout() { return timeout_; } + void set_timeout(int timeout) { timeout_ = timeout; } + + // Fail redirects to allow analysis of redirect urls, etc. + bool fail_redirect() const { return fail_redirect_; } + void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; } + + HttpRequestData& request() { return client_.request(); } + HttpResponseData& response() { return client_.response(); } + +private: + // SignalThread Interface + virtual void DoWork(); + + talk_base::ProxyInfo proxy_; + talk_base::FirewallManager * firewall_; + std::string host_; + int port_; + bool secure_; + int timeout_; + bool fail_redirect_; + HttpClient client_; + std::string response_redirect_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// HttpMonitor +/////////////////////////////////////////////////////////////////////////////// + +class HttpMonitor : public sigslot::has_slots<> { +public: + HttpMonitor(SocketServer *ss); + + void reset() { complete_ = false; } + + bool done() const { return complete_; } + int error() const { return err_; } + + void Connect(talk_base::HttpClient* http); + void OnHttpClientComplete(talk_base::HttpClient * http, int err); + +private: + bool complete_; + int err_; + SocketServer *ss_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SslSocketFactory +/////////////////////////////////////////////////////////////////////////////// + +class SslSocketFactory : public talk_base::SocketFactory { + public: + SslSocketFactory(talk_base::SocketFactory * factory, const std::string &user_agent) + : factory_(factory), logging_level_(talk_base::LS_VERBOSE), + binary_mode_(false), agent_(user_agent) { } + + void UseSSL(const char * hostname) { hostname_ = hostname; } + void DisableSSL() { hostname_.clear(); } + + void SetProxy(const talk_base::ProxyInfo& proxy) { proxy_ = proxy; } + const talk_base::ProxyInfo& proxy() const { return proxy_; } + bool ignore_bad_cert() {return ignore_bad_cert_;} + void SetIgnoreBadCert(bool ignore) { ignore_bad_cert_ = ignore; } + + void SetLogging(talk_base::LoggingSeverity level, const std::string& label, + bool binary_mode = false) { + logging_level_ = level; + logging_label_ = label; + binary_mode_ = binary_mode; + } + + virtual talk_base::Socket * CreateSocket(int type); + virtual talk_base::AsyncSocket * CreateAsyncSocket(int type); + +private: + talk_base::SocketFactory * factory_; + talk_base::ProxyInfo proxy_; + std::string hostname_, logging_label_; + talk_base::LoggingSeverity logging_level_; + bool binary_mode_; + std::string agent_; + bool ignore_bad_cert_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base_ + +#endif // _ASYNCHTTPREQUEST_H_ diff --git a/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc new file mode 100644 index 0000000..37ba058 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.cc @@ -0,0 +1,84 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include "talk/base/asyncpacketsocket.h" + +namespace talk_base { + +AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) { +} + +AsyncPacketSocket::~AsyncPacketSocket() { + delete socket_; +} + +SocketAddress AsyncPacketSocket::GetLocalAddress() const { + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncPacketSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncPacketSocket::Bind(const SocketAddress& addr) { + return socket_->Bind(addr); +} + +int AsyncPacketSocket::Connect(const SocketAddress& addr) { + return socket_->Connect(addr); +} + +int AsyncPacketSocket::Send(const void *pv, size_t cb) { + return socket_->Send(pv, cb); +} + +int AsyncPacketSocket::SendTo( + const void *pv, size_t cb, const SocketAddress& addr) { + return socket_->SendTo(pv, cb, addr); +} + +int AsyncPacketSocket::Close() { + return socket_->Close(); +} + +int AsyncPacketSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncPacketSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncPacketSocket::SetError(int error) { + return socket_->SetError(error); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h new file mode 100644 index 0000000..c6172a7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncpacketsocket.h @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_ASYNCPACKETSOCKET_H__ +#define TALK_BASE_ASYNCPACKETSOCKET_H__ + +#include "talk/base/asyncsocket.h" + +namespace talk_base { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncPacketSocket : public sigslot::has_slots<> { +public: + AsyncPacketSocket(AsyncSocket* socket); + virtual ~AsyncPacketSocket(); + + // Relevant socket methods: + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Bind(const SocketAddress& addr); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Close(); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + + // Emitted each time a packet is read. + sigslot::signal4<const char*, size_t, const SocketAddress&, AsyncPacketSocket*> SignalReadPacket; + +protected: + AsyncSocket* socket_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCPACKETSOCKET_H__ diff --git a/Plugins/jingle/libjingle/talk/base/asyncsocket.h b/Plugins/jingle/libjingle/talk/base/asyncsocket.h new file mode 100644 index 0000000..ad0dfb4 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncsocket.h @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_ASYNCSOCKET_H__ +#define TALK_BASE_ASYNCSOCKET_H__ + +#include "talk/base/sigslot.h" +#include "talk/base/socket.h" + +namespace talk_base { + +// Provides the ability to perform socket I/O asynchronously. +class AsyncSocket : public Socket, public sigslot::has_slots<> { +public: + virtual ~AsyncSocket() {} + + sigslot::signal1<AsyncSocket*> SignalReadEvent; // ready to read + sigslot::signal1<AsyncSocket*> SignalWriteEvent; // ready to write + sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected + sigslot::signal2<AsyncSocket*,int> SignalCloseEvent; // closed + // TODO: error +}; + +class AsyncSocketAdapter : public AsyncSocket { +public: + AsyncSocketAdapter(Socket * socket) : socket_(socket) { + } + AsyncSocketAdapter(AsyncSocket * socket) : socket_(socket) { + socket->SignalConnectEvent.connect(this, &AsyncSocketAdapter::OnConnectEvent); + socket->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent); + socket->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent); + socket->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent); + } + virtual ~AsyncSocketAdapter() { delete socket_; } + + virtual SocketAddress GetLocalAddress() const { return socket_->GetLocalAddress(); } + virtual SocketAddress GetRemoteAddress() const { return socket_->GetRemoteAddress(); } + + virtual int Bind(const SocketAddress& addr) { return socket_->Bind(addr); } + virtual int Connect(const SocketAddress& addr) {return socket_->Connect(addr); } + virtual int Send(const void *pv, size_t cb) { return socket_->Send(pv, cb); } + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { return socket_->SendTo(pv, cb, addr); } + virtual int Recv(void *pv, size_t cb) { return socket_->Recv(pv, cb); } + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { return socket_->RecvFrom(pv, cb, paddr); } + virtual int Listen(int backlog) { return socket_->Listen(backlog); } + virtual Socket *Accept(SocketAddress *paddr) { return socket_->Accept(paddr); } + virtual int Close() { return socket_->Close(); } + virtual int GetError() const { return socket_->GetError(); } + virtual void SetError(int error) { return socket_->SetError(error); } + + virtual ConnState GetState() const { return socket_->GetState(); } + + virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); } + virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); } + +protected: + virtual void OnConnectEvent(AsyncSocket * socket) { SignalConnectEvent(this); } + virtual void OnReadEvent(AsyncSocket * socket) { SignalReadEvent(this); } + virtual void OnWriteEvent(AsyncSocket * socket) { SignalWriteEvent(this); } + virtual void OnCloseEvent(AsyncSocket * socket, int err) { SignalCloseEvent(this, err); } + + Socket * socket_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCSOCKET_H__ diff --git a/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc new file mode 100644 index 0000000..84d1401 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.cc @@ -0,0 +1,200 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include "talk/base/asynctcpsocket.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace talk_base { + +const size_t MAX_PACKET_SIZE = 64 * 1024; + +typedef uint16 PacketLength; +const size_t PKT_LEN_SIZE = sizeof(PacketLength); + +const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; + +AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) { + inbuf_ = new char[insize_]; + outbuf_ = new char[outsize_]; + + ASSERT(socket_ != NULL); + socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent); +} + +AsyncTCPSocket::~AsyncTCPSocket() { + delete [] inbuf_; + delete [] outbuf_; +} + +int AsyncTCPSocket::Send(const void *pv, size_t cb) { + if (cb > MAX_PACKET_SIZE) { + socket_->SetError(EMSGSIZE); + return -1; + } + + // If we are blocking on send, then silently drop this packet + if (outpos_) + return static_cast<int>(cb); + + PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb)); + memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE); + memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb); + outpos_ = PKT_LEN_SIZE + cb; + + int res = Flush(); + if (res <= 0) { + // drop packet if we made no progress + outpos_ = 0; + return res; + } + + // We claim to have sent the whole thing, even if we only sent partial + return static_cast<int>(cb); +} + +int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + if (addr == GetRemoteAddress()) + return Send(pv, cb); + + ASSERT(false); + socket_->SetError(ENOTCONN); + return -1; +} + +int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) { + if (outpos_ + cb > outsize_) { + socket_->SetError(EMSGSIZE); + return -1; + } + + memcpy(outbuf_ + outpos_, pv, cb); + outpos_ += cb; + + return Flush(); +} + +void AsyncTCPSocket::ProcessInput(char * data, size_t& len) { + SocketAddress remote_addr(GetRemoteAddress()); + + while (true) { + if (len < PKT_LEN_SIZE) + return; + + PacketLength pkt_len; + memcpy(&pkt_len, data, PKT_LEN_SIZE); + pkt_len = NetworkToHost16(pkt_len); + + if (len < PKT_LEN_SIZE + pkt_len) + return; + + SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this); + + len -= PKT_LEN_SIZE + pkt_len; + if (len > 0) { + memmove(data, data + PKT_LEN_SIZE + pkt_len, len); + } + } +} + +int AsyncTCPSocket::Flush() { + int res = socket_->Send(outbuf_, outpos_); + if (res <= 0) { + return res; + } + if (static_cast<size_t>(res) <= outpos_) { + outpos_ -= res; + } else { + ASSERT(false); + return -1; + } + if (outpos_ > 0) { + memmove(outbuf_, outbuf_ + res, outpos_); + } + return res; +} + +void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) { + SignalConnect(this); +} + +void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + if (!socket_->IsBlocking()) { + LOG(LS_ERROR) << "recvfrom: " << errno << " " << std::strerror(errno); + } + return; + } + + inpos_ += len; + + ProcessInput(inbuf_, inpos_); + + if (inpos_ >= insize_) { + LOG(INFO) << "input buffer overflow"; + ASSERT(false); + inpos_ = 0; + } +} + +void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + + if (outpos_ > 0) { + Flush(); + } +} + +void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) { + SignalClose(this, error); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h new file mode 100644 index 0000000..2509451 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asynctcpsocket.h @@ -0,0 +1,68 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_ASYNCTCPSOCKET_H__ +#define TALK_BASE_ASYNCTCPSOCKET_H__ + +#include "talk/base/asyncpacketsocket.h" + +namespace talk_base { + +// Simulates UDP semantics over TCP. Send and Recv packet sizes +// are preserved, and drops packets silently on Send, rather than +// buffer them in user space. +class AsyncTCPSocket : public AsyncPacketSocket { +public: + AsyncTCPSocket(AsyncSocket* socket); + virtual ~AsyncTCPSocket(); + + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + + sigslot::signal1<AsyncTCPSocket*> SignalConnect; + sigslot::signal2<AsyncTCPSocket*,int> SignalClose; + +protected: + int SendRaw(const void * pv, size_t cb); + virtual void ProcessInput(char * data, size_t& len); + +private: + char* inbuf_, * outbuf_; + size_t insize_, inpos_, outsize_, outpos_; + + int Flush(); + + // Called by the underlying socket + void OnConnectEvent(AsyncSocket* socket); + void OnReadEvent(AsyncSocket* socket); + void OnWriteEvent(AsyncSocket* socket); + void OnCloseEvent(AsyncSocket* socket, int error); +}; + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCTCPSOCKET_H__ diff --git a/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc new file mode 100644 index 0000000..a0c967d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.cc @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <cassert> +#include <cstring> +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/logging.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +namespace talk_base { + +const int BUF_SIZE = 64 * 1024; + +AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) { + size_ = BUF_SIZE; + buf_ = new char[size_]; + + assert(socket_); + // The socket should start out readable but not writable. + socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent); +} + +AsyncUDPSocket::~AsyncUDPSocket() { + delete [] buf_; +} + +void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { + assert(socket == socket_); + + SocketAddress remote_addr; + int len = socket_->RecvFrom(buf_, size_, &remote_addr); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + PLOG(LS_ERROR, socket_->GetError()) << "recvfrom"; + return; + } + + // TODO: Make sure that we got all of the packet. If we did not, then we + // should resize our buffer to be large enough. + + SignalReadPacket(buf_, (size_t)len, remote_addr, this); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h new file mode 100644 index 0000000..8232938 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/asyncudpsocket.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_ASYNCUDPSOCKET_H__ +#define TALK_BASE_ASYNCUDPSOCKET_H__ + +#include "talk/base/asyncpacketsocket.h" +#include "talk/base/socketfactory.h" + +namespace talk_base { + +// Provides the ability to receive packets asynchronously. Sends are not +// buffered since it is acceptable to drop packets under high load. +class AsyncUDPSocket : public AsyncPacketSocket { +public: + AsyncUDPSocket(AsyncSocket* socket); + virtual ~AsyncUDPSocket(); + +private: + char* buf_; + size_t size_; + + // Called when the underlying socket is ready to be read from. + void OnReadEvent(AsyncSocket* socket); +}; + +// Creates a new socket for sending asynchronous UDP packets using an +// asynchronous socket from the given factory. +inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) { + return new AsyncUDPSocket(factory->CreateAsyncSocket(SOCK_DGRAM)); +} + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCUDPSOCKET_H__ diff --git a/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc b/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc new file mode 100644 index 0000000..3b8e6f6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/autodetectproxy.cc @@ -0,0 +1,178 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/autodetectproxy.h" +#include "talk/base/httpcommon.h" +//#include "talk/xmpp/xmppclientsettings.h" +#include "talk/base/proxydetect.h" + +using namespace talk_base; + +enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE }; + +const talk_base::ProxyType TEST_ORDER[] = { + talk_base::PROXY_HTTPS, /*talk_base::PROXY_SOCKS5,*/ talk_base::PROXY_UNKNOWN +}; + +AutoDetectProxy::AutoDetectProxy(const std::string& user_agent) +: agent_(user_agent), socket_(NULL), next_(0) +{ +} + +AutoDetectProxy::~AutoDetectProxy() { + delete socket_; +} + +void AutoDetectProxy::DoWork() { + if (!server_url_.empty()) { + LOG(LS_INFO) << "GetProxySettingsForUrl(" << server_url_ << ") - start"; + GetProxySettingsForUrl(agent_.c_str(), server_url_.c_str(), proxy_, true); + LOG(LS_INFO) << "GetProxySettingsForUrl - stop"; + } + talk_base::Url<char> url(proxy_.address.IPAsString()); + if (url.valid()) { + LOG(LS_WARNING) << "AutoDetectProxy removing http prefix on proxy host"; + proxy_.address.SetIP(url.server()); + } + if (proxy_.type == talk_base::PROXY_UNKNOWN) { + //LOG(LS_INFO) << "Proxy classification start"; + Next(); + // Process I/O until Stop() + Thread::Current()->ProcessMessages(kForever); + } +} + +void AutoDetectProxy::OnMessage(Message *msg) { + if (MSG_TIMEOUT == msg->message_id) { + OnCloseEvent(socket_, ETIMEDOUT); + } else { + SignalThread::OnMessage(msg); + } +} + +void AutoDetectProxy::Next() { + if (TEST_ORDER[next_] >= talk_base::PROXY_UNKNOWN) { + Complete(talk_base::PROXY_UNKNOWN); + return; + } + + LOG(LS_VERBOSE) << "AutoDetectProxy connecting to " + << proxy_.address.ToString(); + + if (socket_) { + Thread::Current()->Clear(this, MSG_TIMEOUT); + socket_->Close(); + Thread::Current()->Dispose(socket_); + socket_ = NULL; + } + + socket_ = Thread::Current()->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent); + socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent); + socket_->Connect(proxy_.address); + + // Timeout after 2 seconds + Thread::Current()->PostDelayed(2000, this, MSG_TIMEOUT); +} + +void AutoDetectProxy::Complete(talk_base::ProxyType type) { + Thread::Current()->Clear(this, MSG_TIMEOUT); + socket_->Close(); + + proxy_.type = type; + talk_base::LoggingSeverity sev + = (proxy_.type == talk_base::PROXY_UNKNOWN) + ? talk_base::LS_ERROR : talk_base::LS_VERBOSE; + LOG_V(sev) << "AutoDetectProxy detected " << proxy_.address.ToString() + << " as type " << proxy_.type; + + Thread::Current()->MessageQueue::Stop(); +} + +void AutoDetectProxy::OnConnectEvent(talk_base::AsyncSocket * socket) { + std::string probe; + + switch (TEST_ORDER[next_]) { + case talk_base::PROXY_HTTPS: + probe.assign("\005\001" + "CONNECT www.google.com:443 HTTP/1.0\r\n" + "User-Agent: "); + probe.append(agent_); + probe.append("\r\n" + "Host: www.google.com\r\n" + "Content-Length: 0\r\n" + "Proxy-Connection: Keep-Alive\r\n" + "\r\n"); + //probe = "CONNECT www.google.com:443 HTTP/1.0\r\n\r\n"; + break; + case talk_base::PROXY_SOCKS5: + probe.assign("\005\001\000", 3); + break; + } + + LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_] + << " sending " << probe.size() << " bytes"; + socket_->Send(probe.data(), probe.size()); +} + +void AutoDetectProxy::OnReadEvent(talk_base::AsyncSocket * socket) { + char data[257]; + int len = socket_->Recv(data, 256); + if (len > 0) { + data[len] = 0; + LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes"; + } + + switch (TEST_ORDER[next_]) { + case talk_base::PROXY_HTTPS: + if ((len >= 2) && (data[0] == '\x05')) { + Complete(talk_base::PROXY_SOCKS5); + return; + } + if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) { + Complete(talk_base::PROXY_HTTPS); + return; + } + break; + case talk_base::PROXY_SOCKS5: + if ((len >= 2) && (data[0] == '\x05')) { + Complete(talk_base::PROXY_SOCKS5); + return; + } + break; + } + + ++next_; + Next(); +} + +void AutoDetectProxy::OnCloseEvent(talk_base::AsyncSocket * socket, int error) { + LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error; + ++next_; + Next(); +} diff --git a/Plugins/jingle/libjingle/talk/base/autodetectproxy.h b/Plugins/jingle/libjingle/talk/base/autodetectproxy.h new file mode 100644 index 0000000..9633d31 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/autodetectproxy.h @@ -0,0 +1,68 @@ +#ifndef _AUTODETECTPROXY_H_ +#define _AUTODETECTPROXY_H_ + +#include "talk/base/sigslot.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/proxyinfo.h" +#include "talk/base/signalthread.h" +#include "talk/base/cryptstring.h" + +namespace buzz { +class XmppClientSettings; +} + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// AutoDetectProxy +/////////////////////////////////////////////////////////////////////////////// + +class AsyncSocket; + +class AutoDetectProxy : public SignalThread, public sigslot::has_slots<> { +public: + AutoDetectProxy(const std::string& user_agent); + + const talk_base::ProxyInfo& proxy() const { return proxy_; } + + void set_server_url(const std::string& url) { + server_url_ = url; + } + void set_proxy(const SocketAddress& proxy) { + proxy_.type = PROXY_UNKNOWN; + proxy_.address = proxy; + } + void set_auth_info(bool use_auth, const std::string& username, + const CryptString& password) { + if (use_auth) { + proxy_.username = username; + proxy_.password = password; + } + } + +protected: + virtual ~AutoDetectProxy(); + + // SignalThread Interface + virtual void DoWork(); + virtual void OnMessage(Message *msg); + + void Next(); + void Complete(ProxyType type); + + void OnConnectEvent(AsyncSocket * socket); + void OnReadEvent(AsyncSocket * socket); + void OnCloseEvent(AsyncSocket * socket, int error); + +private: + std::string agent_, server_url_; + ProxyInfo proxy_; + AsyncSocket * socket_; + int next_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // _AUTODETECTPROXY_H_ diff --git a/Plugins/jingle/libjingle/talk/base/base64.cc b/Plugins/jingle/libjingle/talk/base/base64.cc new file mode 100644 index 0000000..53e5bac --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/base64.cc @@ -0,0 +1,196 @@ + +//********************************************************************* +//* Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//* +//* Enhancements by Stanley Yamane: +//* o reverse lookup table for the decode function +//* o reserve string buffer space in advance +//* +//********************************************************************* + +#include "talk/base/base64.h" + +using std::string; + +namespace talk_base { + +static const char fillchar = '='; +static const string::size_type np = string::npos; + +const string Base64::Base64Table( + // 0000000000111111111122222222223333333333444444444455555555556666 + // 0123456789012345678901234567890123456789012345678901234567890123 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + +// Decode Table gives the index of any valid base64 character in the +// Base64 table +// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == / + +const string::size_type Base64::DecodeTable[] = { +// 0 1 2 3 4 5 6 7 8 9 + np,np,np,np,np,np,np,np,np,np, // 0 - 9 + np,np,np,np,np,np,np,np,np,np, //10 -19 + np,np,np,np,np,np,np,np,np,np, //20 -29 + np,np,np,np,np,np,np,np,np,np, //30 -39 + np,np,np,62,np,np,np,63,52,53, //40 -49 + 54,55,56,57,58,59,60,61,np,np, //50 -59 + np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69 + 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79 + 15,16,17,18,19,20,21,22,23,24, //80 -89 + 25,np,np,np,np,np,np,26,27,28, //90 -99 + 29,30,31,32,33,34,35,36,37,38, //100 -109 + 39,40,41,42,43,44,45,46,47,48, //110 -119 + 49,50,51,np,np,np,np,np,np,np, //120 -129 + np,np,np,np,np,np,np,np,np,np, //130 -139 + np,np,np,np,np,np,np,np,np,np, //140 -149 + np,np,np,np,np,np,np,np,np,np, //150 -159 + np,np,np,np,np,np,np,np,np,np, //160 -169 + np,np,np,np,np,np,np,np,np,np, //170 -179 + np,np,np,np,np,np,np,np,np,np, //180 -189 + np,np,np,np,np,np,np,np,np,np, //190 -199 + np,np,np,np,np,np,np,np,np,np, //200 -209 + np,np,np,np,np,np,np,np,np,np, //210 -219 + np,np,np,np,np,np,np,np,np,np, //220 -229 + np,np,np,np,np,np,np,np,np,np, //230 -239 + np,np,np,np,np,np,np,np,np,np, //240 -249 + np,np,np,np,np,np //250 -256 +}; + +string Base64::encodeFromArray(const char * data, size_t len) { + size_t i; + char c; + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) { + c |= (data[i] >> 4) & 0x0f; + } + + ret.append(1, Base64Table[c]); + if (i < len) { + c = (data[i] << 2) & 0x3f; + if (++i < len) { + c |= (data[i] >> 6) & 0x03; + } + ret.append(1, Base64Table[c]); + } else { + ++i; + ret.append(1, fillchar); + } + + if (i < len) { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } else { + ret.append(1, fillchar); + } + } + + return(ret); +} + +string Base64::encode(const string& data) { + string::size_type i; + char c; + string::size_type len = data.length(); + string ret; + + ret.reserve(len * 2); + + for (i = 0; i < len; ++i) { + c = (data[i] >> 2) & 0x3f; + ret.append(1, Base64Table[c]); + c = (data[i] << 4) & 0x3f; + if (++i < len) { + c |= (data[i] >> 4) & 0x0f; + } + + ret.append(1, Base64Table[c]); + if (i < len) { + c = (data[i] << 2) & 0x3f; + if (++i < len) { + c |= (data[i] >> 6) & 0x03; + } + + ret.append(1, Base64Table[c]); + } else { + ++i; + ret.append(1, fillchar); + } + + if (i < len) { + c = data[i] & 0x3f; + ret.append(1, Base64Table[c]); + } else { + ret.append(1, fillchar); + } + } + + return(ret); +} + +string Base64::decode(const string& data) { + string::size_type i; + char c; + char c1; + string::size_type len = data.length(); + string ret; + + ret.reserve(len); + + for (i = 0; i < len; ++i) { + do { + c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + } while (c == np && ++i < len); + + ++i; + + do { + c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + } while (c == np && ++i < len); + + c = (c << 2) | ((c1 >> 4) & 0x3); + ret.append(1, c); + if (++i < len) { + c = data[i]; + if (fillchar == c) { + break; + } + + do { + c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + } while (c == np && ++i < len); + c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); + ret.append(1, c1); + } + + if (++i < len) { + c1 = data[i]; + if (fillchar == c1) { + break; + } + + do { + c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]); + } while (c == np && ++i < len); + + c = ((c << 6) & 0xc0) | c1; + ret.append(1, c); + } + } + + return(ret); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/base64.h b/Plugins/jingle/libjingle/talk/base/base64.h new file mode 100644 index 0000000..2b58761 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/base64.h @@ -0,0 +1,32 @@ + +//********************************************************************* +//* C_Base64 - a simple base64 encoder and decoder. +//* +//* Copyright (c) 1999, Bob Withers - bwit@pobox.com +//* +//* This code may be freely used for any purpose, either personal +//* or commercial, provided the authors copyright notice remains +//* intact. +//********************************************************************* + +#ifndef TALK_BASE_BASE64_H__ +#define TALK_BASE_BASE64_H__ + +#include <string> + +namespace talk_base { + +class Base64 +{ +public: + static std::string encode(const std::string & data); + static std::string decode(const std::string & data); + static std::string encodeFromArray(const char * data, size_t len); +private: + static const std::string Base64::Base64Table; + static const std::string::size_type Base64::DecodeTable[]; +}; + +} // namespace talk_base + +#endif // TALK_BASE_BASE64_H__ diff --git a/Plugins/jingle/libjingle/talk/base/basicdefs.h b/Plugins/jingle/libjingle/talk/base/basicdefs.h new file mode 100644 index 0000000..886c287 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/basicdefs.h @@ -0,0 +1,37 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_BASICDEFS_H__ +#define TAKL_BASE_BASICDEFS_H__ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0])))) + +#endif // TAKL_BASE_BASICDEFS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/basictypes.h b/Plugins/jingle/libjingle/talk/base/basictypes.h new file mode 100644 index 0000000..47588ad --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/basictypes.h @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_BASICTYPES_H__ +#define TALK_BASE_BASICTYPES_H__ + +#ifdef COMPILER_MSVC +typedef __int64 int64; +#else +typedef long long int64; +#endif /* COMPILER_MSVC */ +typedef long int32; +typedef short int16; +typedef char int8; + +#ifdef COMPILER_MSVC +typedef unsigned __int64 uint64; +typedef __int64 int64; +#else +typedef unsigned long long uint64; +typedef long long int64; +#endif /* COMPILER_MSVC */ +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +#ifdef WIN32 +typedef int socklen_t; +#endif + +namespace talk_base { + template<class T> inline T _min(T a, T b) { return (a > b) ? b : a; } + template<class T> inline T _max(T a, T b) { return (a < b) ? b : a; } +} + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_EVIL_CONSTRUCTORS(TypeName) + +#ifndef UNUSED +#define UNUSED(x) Unused(static_cast<const void *>(&x)) +#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)) +#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)) +#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)) +#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b)) +inline void Unused(const void *) { } +#endif // UNUSED + +#endif // TALK_BASE_BASICTYPES_H__ diff --git a/Plugins/jingle/libjingle/talk/base/bytebuffer.cc b/Plugins/jingle/libjingle/talk/base/bytebuffer.cc new file mode 100644 index 0000000..7082e1a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/bytebuffer.cc @@ -0,0 +1,166 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> +#include <cassert> + +#include "talk/base/basictypes.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/byteorder.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +namespace talk_base { + +static const int DEFAULT_SIZE = 4096; + +ByteBuffer::ByteBuffer() { + start_ = 0; + end_ = 0; + size_ = DEFAULT_SIZE; + bytes_ = new char[size_]; +} + +ByteBuffer::ByteBuffer(const char* bytes, size_t len) { + start_ = 0; + end_ = len; + size_ = len; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::ByteBuffer(const char* bytes) { + start_ = 0; + end_ = strlen(bytes); + size_ = end_; + bytes_ = new char[size_]; + memcpy(bytes_, bytes, end_); +} + +ByteBuffer::~ByteBuffer() { + delete bytes_; +} + +bool ByteBuffer::ReadUInt8(uint8& val) { + return ReadBytes(reinterpret_cast<char*>(&val), 1); +} + +bool ByteBuffer::ReadUInt16(uint16& val) { + uint16 v; + if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) { + return false; + } else { + val = NetworkToHost16(v); + return true; + } +} + +bool ByteBuffer::ReadUInt32(uint32& val) { + uint32 v; + if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) { + return false; + } else { + val = NetworkToHost32(v); + return true; + } +} + +bool ByteBuffer::ReadString(std::string& val, size_t len) { + if (len > Length()) { + return false; + } else { + val.append(bytes_ + start_, len); + start_ += len; + return true; + } +} + +bool ByteBuffer::ReadBytes(char* val, size_t len) { + if (len > Length()) { + return false; + } else { + memcpy(val, bytes_ + start_, len); + start_ += len; + return true; + } +} + +void ByteBuffer::WriteUInt8(uint8 val) { + WriteBytes(reinterpret_cast<const char*>(&val), 1); +} + +void ByteBuffer::WriteUInt16(uint16 val) { + uint16 v = HostToNetwork16(val); + WriteBytes(reinterpret_cast<const char*>(&v), 2); +} + +void ByteBuffer::WriteUInt32(uint32 val) { + uint32 v = HostToNetwork32(val); + WriteBytes(reinterpret_cast<const char*>(&v), 4); +} + +void ByteBuffer::WriteString(const std::string& val) { + WriteBytes(val.c_str(), val.size()); +} + +void ByteBuffer::WriteBytes(const char* val, size_t len) { + if (Length() + len > Capacity()) + Resize(Length() + len); + + memcpy(bytes_ + end_, val, len); + end_ += len; +} + +void ByteBuffer::Resize(size_t size) { + if (size > size_) + size = _max(size, 3 * size_ / 2); + + size_t len = _min(end_ - start_, size); + char* new_bytes = new char[size]; + memcpy(new_bytes, bytes_ + start_, len); + delete [] bytes_; + + start_ = 0; + end_ = len; + size_ = size; + bytes_ = new_bytes; +} + +void ByteBuffer::Shift(size_t size) { + if (size > Length()) + return; + + end_ = Length() - size; + memmove(bytes_, bytes_ + start_ + size, end_); + start_ = 0; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/bytebuffer.h b/Plugins/jingle/libjingle/talk/base/bytebuffer.h new file mode 100644 index 0000000..0a93194 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/bytebuffer.h @@ -0,0 +1,71 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_BYTEBUFFER_H__ +#define TALK_BASE_BYTEBUFFER_H__ + +#include <string> +#include "talk/base/basictypes.h" + +namespace talk_base { + +class ByteBuffer { +public: + ByteBuffer(); + ByteBuffer(const char* bytes, size_t len); + ByteBuffer(const char* bytes); // uses strlen + ~ByteBuffer(); + + const char* Data() const { return bytes_ + start_; } + size_t Length() { return end_ - start_; } + size_t Capacity() { return size_ - start_; } + + bool ReadUInt8(uint8& val); + bool ReadUInt16(uint16& val); + bool ReadUInt32(uint32& val); + bool ReadString(std::string& val, size_t len); // append to val + bool ReadBytes(char* val, size_t len); + + void WriteUInt8(uint8 val); + void WriteUInt16(uint16 val); + void WriteUInt32(uint32 val); + void WriteString(const std::string& val); + void WriteBytes(const char* val, size_t len); + + void Resize(size_t size); + void Shift(size_t size); + +private: + char* bytes_; + size_t size_; + size_t start_; + size_t end_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_BYTEBUFFER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/byteorder.h b/Plugins/jingle/libjingle/talk/base/byteorder.h new file mode 100644 index 0000000..16834c2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/byteorder.h @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_BYTEORDER_H__ +#define TALK_BASE_BYTEORDER_H__ + +#include "talk/base/basictypes.h" + +#ifdef POSIX +extern "C" { +#include <arpa/inet.h> +} +#endif + +#ifdef WIN32 +#include <winsock2.h> +#endif + +namespace talk_base { + +inline uint16 HostToNetwork16(uint16 n) { + return htons(n); +} + +inline uint32 HostToNetwork32(uint32 n) { + return htonl(n); +} + +inline uint16 NetworkToHost16(uint16 n) { + return ntohs(n); +} + +inline uint32 NetworkToHost32(uint32 n) { + return ntohl(n); +} + +} // namespace talk_base + +#endif // TALK_BASE_BYTEORDER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/common.cc b/Plugins/jingle/libjingle/talk/base/common.cc new file mode 100644 index 0000000..aa5c56a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/common.cc @@ -0,0 +1,62 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#ifdef WIN32 +#include <windows.h> +#endif // WIN32 + +#include <algorithm> + +#include "talk/base/common.h" + +////////////////////////////////////////////////////////////////////// +// Assertions +////////////////////////////////////////////////////////////////////// + +namespace talk_base { + +void Break() { +#ifdef WIN32 + ::DebugBreak(); +#else // !WIN32 +#if _DEBUG_HAVE_BACKTRACE + OutputTrace(); +#endif + abort(); +#endif // !WIN32 +} + +void LogAssert(const char * function, const char * file, int line, const char * expression) { + // TODO - if we put hooks in here, we can do a lot fancier logging + fprintf(stderr, "%s(%d): %s @ %s\n", file, line, expression, function); +} + +} // namespace talk_base
\ No newline at end of file diff --git a/Plugins/jingle/libjingle/talk/base/common.h b/Plugins/jingle/libjingle/talk/base/common.h new file mode 100644 index 0000000..d652f81 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/common.h @@ -0,0 +1,120 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_COMMON_H__ +#define TALK_BASE_COMMON_H__ + +#if defined(_MSC_VER) +// warning C4355: 'this' : used in base member initializer list +#pragma warning(disable:4355) +#endif + +////////////////////////////////////////////////////////////////////// +// General Utilities +////////////////////////////////////////////////////////////////////// + +#ifndef UNUSED +#define UNUSED(x) Unused(static_cast<const void *>(&x)) +#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)) +#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)) +#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)) +#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b)) +inline void Unused(const void *) { } +#endif // UNUSED + +#ifndef WIN32 +#define strnicmp(x,y,n) strncasecmp(x,y,n) +#define stricmp(x,y) strcasecmp(x,y) +#define stdmax(x,y) std::max(x,y) +#else +#define stdmax(x,y) max(x,y) +#endif + + +#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0])))) + +///////////////////////////////////////////////////////////////////////////// +// Assertions +///////////////////////////////////////////////////////////////////////////// + +#ifdef ENABLE_DEBUG + +namespace talk_base { + +// Break causes the debugger to stop executing, or the program to abort +void Break(); + +// LogAssert writes information about an assertion to the log +void LogAssert(const char * function, const char * file, int line, const char * expression); + +inline void Assert(bool result, const char * function, const char * file, int line, const char * expression) { + if (!result) { + LogAssert(function, file, line, expression); + Break(); + } +} + +}; // namespace talk_base + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define __FUNCTION__ "" +#endif + +#ifndef ASSERT +#define ASSERT(x) talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x) +#endif + +#ifndef VERIFY +#define VERIFY(x) talk_base::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x) +#endif + +#else // !ENABLE_DEBUG + +#ifndef ASSERT +#define ASSERT(x) (void)0 +#endif + +#ifndef VERIFY +#define VERIFY(x) (void)(x) +#endif + +#endif // !ENABLE_DEBUG + +#define COMPILE_TIME_ASSERT(expr) char CTA_UNIQUE_NAME[expr] +#define CTA_UNIQUE_NAME MAKE_NAME(__LINE__) +#define CTA_MAKE_NAME(line) MAKE_NAME2(line) +#define CTA_MAKE_NAME2(line) constraint_ ## line + +////////////////////////////////////////////////////////////////////// + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#endif // TALK_BASE_COMMON_H__ diff --git a/Plugins/jingle/libjingle/talk/base/convert.h b/Plugins/jingle/libjingle/talk/base/convert.h new file mode 100644 index 0000000..26c6d32 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/convert.h @@ -0,0 +1,149 @@ +/* + * libjingle + * Copyright 2007, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CONVERT_H_ +#define _CONVERT_H_ + +#include <string> +#include <windows.h> + +#ifndef NO_ATL +#include <atlstr.h> +#endif // NO_ATL + +#include "talk/base/basictypes.h" + +class Utf8 { +public: +#ifndef NO_ATL + explicit Utf8(const CString & str) { + *this = str; + } +#else + explicit Utf8(const wchar_t *str) { + *this = str; + } +#endif + + explicit Utf8() {} +#ifndef NO_ATL + inline Utf8& operator =(const CString & str) { + // TODO: deal with errors + int len8 = WideCharToMultiByte(CP_UTF8, 0, str.GetString(), str.GetLength(), + NULL, 0, NULL, NULL); + char * ns = static_cast<char*>(_alloca(len8)); + WideCharToMultiByte(CP_UTF8, 0, str.GetString(), str.GetLength(), + ns, len8, NULL, NULL); + str_.assign(ns, len8); + return *this; + } +#else +inline Utf8& operator =(const wchar_t *str) { + // TODO: deal with errors + int len8 = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), + NULL, 0, NULL, NULL); + char * ns = static_cast<char*>(_alloca(len8)); + WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), + ns, len8, NULL, NULL); + str_.assign(ns, len8); + return *this; + } +#endif // NO_ATL + + inline operator const std::string & () const { + return str_; + } + + inline const char * AsSz() const { + return str_.c_str(); + } + + // Deprecated + inline const std::string & AsString() const { + return str_; + } + + // Deprecated + inline int Len8() const { + return (int)str_.length(); + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(Utf8); + std::string str_; +}; + +class Utf16 { +public: + explicit Utf16(const std::string & str) { + // TODO: deal with errors + int len16 = MultiByteToWideChar(CP_UTF8, 0, str.data(), -1, + NULL, 0); +#ifndef NO_ATL + wchar_t * ws = cstr_.GetBuffer(len16); + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), ws, len16); + cstr_.ReleaseBuffer(len16); +#else + str_ = new wchar_t[len16]; + MultiByteToWideChar(CP_UTF8, 0, str.data(), -1, str_, len16); +#endif + } + +#ifndef NO_ATL + inline operator const CString & () const { + return cstr_; + } + // Deprecated + inline const CString & AsCString() const { + return cstr_; + } + // Deprecated + inline int Len16() const { + return cstr_.GetLength(); + } + inline const wchar_t * AsWz() const { + return cstr_.GetString(); + } +#else + ~Utf16() { + delete[] str_; + } + inline const wchar_t * AsWz() const { + return str_; + } +#endif + +private: + DISALLOW_EVIL_CONSTRUCTORS(Utf16); +#ifndef NO_ATL + CString cstr_; +#else + wchar_t *str_; +#endif +}; + +#endif // _CONVERT_H_ diff --git a/Plugins/jingle/libjingle/talk/base/criticalsection.h b/Plugins/jingle/libjingle/talk/base/criticalsection.h new file mode 100644 index 0000000..fbe7382 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/criticalsection.h @@ -0,0 +1,120 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_CRITICALSECTION_H__ +#define TALK_BASE_CRITICALSECTION_H__ + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +#ifdef POSIX +#include <pthread.h> +#endif + +#ifdef _DEBUG +#define CS_TRACK_OWNER 1 +#endif // _DEBUG + +#if CS_TRACK_OWNER +#define TRACK_OWNER(x) x +#else // !CS_TRACK_OWNER +#define TRACK_OWNER(x) +#endif // !CS_TRACK_OWNER + +namespace talk_base { + +#ifdef WIN32 +class CriticalSection { +public: + CriticalSection() { + InitializeCriticalSection(&crit_); + // Windows docs say 0 is not a valid thread id + TRACK_OWNER(thread_ = 0); + } + ~CriticalSection() { + DeleteCriticalSection(&crit_); + } + void Enter() { + EnterCriticalSection(&crit_); + TRACK_OWNER(thread_ = GetCurrentThreadId()); + } + void Leave() { + TRACK_OWNER(thread_ = 0); + LeaveCriticalSection(&crit_); + } + +#if CS_TRACK_OWNER + bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); } +#endif // CS_TRACK_OWNER + +private: + CRITICAL_SECTION crit_; + TRACK_OWNER(DWORD thread_); // The section's owning thread id +}; +#endif // WIN32 + +#ifdef POSIX +class CriticalSection { +public: + CriticalSection() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &mutex_attribute); + } + ~CriticalSection() { + pthread_mutex_destroy(&mutex_); + } + void Enter() { + pthread_mutex_lock(&mutex_); + } + void Leave() { + pthread_mutex_unlock(&mutex_); + } +private: + pthread_mutex_t mutex_; +}; +#endif // POSIX + +// CritScope, for serializing exection through a scope + +class CritScope { +public: + CritScope(CriticalSection *pcrit) { + pcrit_ = pcrit; + pcrit_->Enter(); + } + ~CritScope() { + pcrit_->Leave(); + } +private: + CriticalSection *pcrit_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_CRITICALSECTION_H__ diff --git a/Plugins/jingle/libjingle/talk/base/cryptstring.h b/Plugins/jingle/libjingle/talk/base/cryptstring.h new file mode 100644 index 0000000..64a7e57 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/cryptstring.h @@ -0,0 +1,185 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _TALK_BASE_CRYPTSTRING_H_ +#define _TALK_BASE_CRYPTSTRING_H_ + +#include <string> +#include "talk/base/linked_ptr.h" +#include "talk/base/scoped_ptr.h" + +namespace talk_base { + +class CryptStringImpl { +public: + virtual ~CryptStringImpl() {} + virtual size_t GetLength() const = 0; + virtual void CopyTo(char * dest, bool nullterminate) const = 0; + virtual std::string UrlEncode() const = 0; + virtual CryptStringImpl * Copy() const = 0; +}; + +class EmptyCryptStringImpl : public CryptStringImpl { +public: + virtual ~EmptyCryptStringImpl() {} + virtual size_t GetLength() const { return 0; } + virtual void CopyTo(char * dest, bool nullterminate) const { + if (nullterminate) { + *dest = '\0'; + } + } + virtual std::string UrlEncode() const { return ""; } + virtual CryptStringImpl * Copy() const { return new EmptyCryptStringImpl(); } +}; + +class CryptString { +public: + CryptString() : impl_(new EmptyCryptStringImpl()) {} + size_t GetLength() const { return impl_->GetLength(); } + void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); } + CryptString(const CryptString & other) : impl_(other.impl_->Copy()) {} + explicit CryptString(const CryptStringImpl & impl) : impl_(impl.Copy()) {} + CryptString & operator=(const CryptString & other) { + if (this != &other) { + impl_.reset(other.impl_->Copy()); + } + return *this; + } + void Clear() { impl_.reset(new EmptyCryptStringImpl()); } + std::string UrlEncode() const { return impl_->UrlEncode(); } + +private: + scoped_ptr<const CryptStringImpl> impl_; +}; + + +// Used for constructing strings where a password is involved and we +// need to ensure that we zero memory afterwards +class FormatCryptString { +public: + FormatCryptString() { + storage_ = new char[32]; + capacity_ = 32; + length_ = 0; + storage_[0] = 0; + } + + void Append(const std::string & text) { + Append(text.data(), text.length()); + } + + void Append(const char * data, size_t length) { + EnsureStorage(length_ + length + 1); + memcpy(storage_ + length_, data, length); + length_ += length; + storage_[length_] = '\0'; + } + + void Append(const CryptString * password) { + size_t len = password->GetLength(); + EnsureStorage(length_ + len + 1); + password->CopyTo(storage_ + length_, true); + length_ += len; + } + + size_t GetLength() { + return length_; + } + + const char * GetData() { + return storage_; + } + + + // Ensures storage of at least n bytes + void EnsureStorage(size_t n) { + if (capacity_ >= n) { + return; + } + + size_t old_capacity = capacity_; + char * old_storage = storage_; + + for (;;) { + capacity_ *= 2; + if (capacity_ >= n) + break; + } + + storage_ = new char[capacity_]; + + if (old_capacity) { + memcpy(storage_, old_storage, length_); + + // zero memory in a way that an optimizer won't optimize it out + old_storage[0] = 0; + for (size_t i = 1; i < old_capacity; i++) { + old_storage[i] = old_storage[i - 1]; + } + delete[] old_storage; + } + } + + ~FormatCryptString() { + if (capacity_) { + storage_[0] = 0; + for (size_t i = 1; i < capacity_; i++) { + storage_[i] = storage_[i - 1]; + } + } + delete[] storage_; + } +private: + char * storage_; + size_t capacity_; + size_t length_; +}; + +class InsecureCryptStringImpl : public CryptStringImpl { + public: + std::string& password() { return password_; } + const std::string& password() const { return password_; } + + virtual ~InsecureCryptStringImpl() {} + virtual size_t GetLength() const { return password_.size(); } + virtual void CopyTo(char * dest, bool nullterminate) const { + memcpy(dest, password_.data(), password_.size()); + if (nullterminate) dest[password_.size()] = 0; + } + virtual std::string UrlEncode() const { return password_; } + virtual CryptStringImpl * Copy() const { + InsecureCryptStringImpl * copy = new InsecureCryptStringImpl; + copy->password() = password_; + return copy; + } + private: + std::string password_; +}; + +} + +#endif // _TALK_BASE_CRYPTSTRING_H_ diff --git a/Plugins/jingle/libjingle/talk/base/diskcache.cc b/Plugins/jingle/libjingle/talk/base/diskcache.cc new file mode 100644 index 0000000..44e37d9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcache.cc @@ -0,0 +1,362 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <time.h> + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +#include "talk/base/basicdefs.h" +#include "talk/base/common.h" +#include "talk/base/diskcache.h" +#include "talk/base/fileutils.h" +#include "talk/base/pathutils.h" +#include "talk/base/stream.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" + +#ifdef _DEBUG +#define TRANSPARENT_CACHE_NAMES 1 +#else // !_DEBUG +#define TRANSPARENT_CACHE_NAMES 0 +#endif // !_DEBUG + +namespace talk_base { + +class DiskCache; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCacheAdapter +/////////////////////////////////////////////////////////////////////////////// + +class DiskCacheAdapter : public StreamAdapterInterface { +public: + DiskCacheAdapter(const DiskCache* cache, const std::string& id, size_t index, + StreamInterface* stream) + : StreamAdapterInterface(stream), cache_(cache), id_(id), index_(index) + { } + virtual ~DiskCacheAdapter() { + Close(); + cache_->ReleaseResource(id_, index_); + } + +private: + const DiskCache* cache_; + std::string id_; + size_t index_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCache +/////////////////////////////////////////////////////////////////////////////// + +DiskCache::DiskCache() : max_cache_(0), total_size_(0), total_accessors_(0) { +} + +DiskCache::~DiskCache() { + ASSERT(0 == total_accessors_); +} + +bool DiskCache::Initialize(const std::string& folder, size_t size) { + if (!folder_.empty() || !Filesystem::CreateFolder(folder)) + return false; + + folder_ = folder; + max_cache_ = size; + ASSERT(0 == total_size_); + + if (!InitializeEntries()) + return false; + + return CheckLimit(); +} + +bool DiskCache::Purge() { + if (folder_.empty()) + return false; + + if (total_accessors_ > 0) { + LOG_F(LS_WARNING) << "Cache files open"; + return false; + } + + if (!PurgeFiles()) + return false; + + map_.clear(); + return true; +} + +bool DiskCache::LockResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, true); + if (LS_LOCKED == entry->lock_state) + return false; + if ((LS_UNLOCKED == entry->lock_state) && (entry->accessors > 0)) + return false; + if ((total_size_ > max_cache_) && !CheckLimit()) { + LOG_F(LS_WARNING) << "Cache overfull"; + return false; + } + entry->lock_state = LS_LOCKED; + return true; +} + +StreamInterface* DiskCache::WriteResource(const std::string& id, size_t index) { + Entry* entry = GetOrCreateEntry(id, false); + if (LS_LOCKED != entry->lock_state) + return NULL; + + size_t previous_size = 0; + std::string filename(IdToFilename(id, index)); + FileStream::GetSize(filename, &previous_size); + ASSERT(previous_size <= entry->size); + if (previous_size > entry->size) { + previous_size = entry->size; + } + + scoped_ptr<FileStream> file(new FileStream); + if (!file->Open(filename, "wb")) { + LOG_F(LS_ERROR) << "Couldn't create cache file"; + return NULL; + } + + entry->streams = stdmax(entry->streams, index + 1); + entry->size -= previous_size; + total_size_ -= previous_size; + + entry->accessors += 1; + total_accessors_ += 1; + return new DiskCacheAdapter(this, id, index, file.release()); +} + +bool DiskCache::UnlockResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, false); + if (LS_LOCKED != entry->lock_state) + return false; + + if (entry->accessors > 0) { + entry->lock_state = LS_UNLOCKING; + } else { + entry->lock_state = LS_UNLOCKED; + entry->last_modified = time(0); + CheckLimit(); + } + return true; +} + +StreamInterface* DiskCache::ReadResource(const std::string& id, + size_t index) const { + const Entry* entry = GetEntry(id); + if (LS_UNLOCKED != entry->lock_state) + return NULL; + if (index >= entry->streams) + return NULL; + + scoped_ptr<FileStream> file(new FileStream); + if (!file->Open(IdToFilename(id, index), "rb")) + return NULL; + + entry->accessors += 1; + total_accessors_ += 1; + return new DiskCacheAdapter(this, id, index, file.release()); +} + +bool DiskCache::HasResource(const std::string& id) const { + const Entry* entry = GetEntry(id); + return (NULL != entry) && (entry->streams > 0); +} + +bool DiskCache::HasResourceStream(const std::string& id, size_t index) const { + const Entry* entry = GetEntry(id); + if ((NULL == entry) || (index >= entry->streams)) + return false; + + std::string filename = IdToFilename(id, index); + + return FileExists(filename); +} + +bool DiskCache::DeleteResource(const std::string& id) { + Entry* entry = GetOrCreateEntry(id, false); + if (!entry) + return true; + + if ((LS_UNLOCKED != entry->lock_state) || (entry->accessors > 0)) + return false; + + bool success = true; + for (size_t index = 0; index < entry->streams; ++index) { + std::string filename = IdToFilename(id, index); + + if (!FileExists(filename)) + continue; + + if (!DeleteFile(filename)) { + LOG_F(LS_ERROR) << "Couldn't remove cache file: " << filename; + success = false; + } + } + + total_size_ -= entry->size; + map_.erase(id); + return success; +} + +bool DiskCache::CheckLimit() { +#ifdef _DEBUG + // Temporary check to make sure everything is working correctly. + size_t cache_size = 0; + for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) { + cache_size += it->second.size; + } + ASSERT(cache_size == total_size_); +#endif // _DEBUG + + // TODO: Replace this with a non-brain-dead algorithm for clearing out the + // oldest resources... something that isn't O(n^2) + while (total_size_ > max_cache_) { + EntryMap::iterator oldest = map_.end(); + for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) { + if ((LS_UNLOCKED != it->second.lock_state) || (it->second.accessors > 0)) + continue; + oldest = it; + break; + } + if (oldest == map_.end()) { + LOG_F(LS_WARNING) << "All resources are locked!"; + return false; + } + for (EntryMap::iterator it = oldest++; it != map_.end(); ++it) { + if (it->second.last_modified < oldest->second.last_modified) { + oldest = it; + } + } + if (!DeleteResource(oldest->first)) { + LOG_F(LS_ERROR) << "Couldn't delete from cache!"; + return false; + } + } + return true; +} + +std::string DiskCache::IdToFilename(const std::string& id, size_t index) const { +#ifdef TRANSPARENT_CACHE_NAMES + // This escapes colons and other filesystem characters, so the user can't open + // special devices (like "COM1:"), or access other directories. + size_t buffer_size = id.length()*3 + 1; + char* buffer = new char[buffer_size]; + encode(buffer, buffer_size, id.data(), id.length(), + unsafe_filename_characters(), '%'); + // TODO: ASSERT(strlen(buffer) < FileSystem::MaxBasenameLength()); +#else // !TRANSPARENT_CACHE_NAMES + // We might want to just use a hash of the filename at some point, both for + // obfuscation, and to avoid both filename length and escaping issues. + ASSERT(false); +#endif // !TRANSPARENT_CACHE_NAMES + + char extension[32]; + sprintfn(extension, ARRAY_SIZE(extension), ".%u", index); + + Pathname pathname; + pathname.SetFolder(folder_); + pathname.SetBasename(buffer); + pathname.SetExtension(extension); + +#ifdef TRANSPARENT_CACHE_NAMES + delete [] buffer; +#endif // TRANSPARENT_CACHE_NAMES + + return pathname.pathname(); +} + +bool DiskCache::FilenameToId(const std::string& filename, std::string* id, + size_t* index) const { + Pathname pathname(filename); + if (1 != sscanf(pathname.extension().c_str(), ".%u", index)) + return false; + + size_t buffer_size = pathname.basename().length() + 1; + char* buffer = new char[buffer_size]; + decode(buffer, buffer_size, pathname.basename().data(), + pathname.basename().length(), '%'); + id->assign(buffer); + delete [] buffer; + return true; +} + +DiskCache::Entry* DiskCache::GetOrCreateEntry(const std::string& id, + bool create) { + EntryMap::iterator it = map_.find(id); + if (it != map_.end()) + return &it->second; + if (!create) + return NULL; + Entry e; + e.lock_state = LS_UNLOCKED; + e.accessors = 0; + e.size = 0; + e.streams = 0; + e.last_modified = time(0); + it = map_.insert(EntryMap::value_type(id, e)).first; + return &it->second; +} + +void DiskCache::ReleaseResource(const std::string& id, size_t index) const { + const Entry* entry = GetEntry(id); + if (!entry) { + LOG_F(LS_WARNING) << "Missing cache entry"; + ASSERT(false); + return; + } + + entry->accessors -= 1; + total_accessors_ -= 1; + + if (LS_UNLOCKED != entry->lock_state) { + // This is safe, because locked resources only issue WriteResource, which + // is non-const. Think about a better way to handle it. + DiskCache* this2 = const_cast<DiskCache*>(this); + Entry* entry2 = this2->GetOrCreateEntry(id, false); + + size_t new_size = 0; + std::string filename(IdToFilename(id, index)); + FileStream::GetSize(filename, &new_size); + entry2->size += new_size; + this2->total_size_ += new_size; + + if ((LS_UNLOCKING == entry->lock_state) && (0 == entry->accessors)) { + entry2->last_modified = time(0); + entry2->lock_state = LS_UNLOCKED; + this2->CheckLimit(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/diskcache.h b/Plugins/jingle/libjingle/talk/base/diskcache.h new file mode 100644 index 0000000..b42e3e0 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcache.h @@ -0,0 +1,142 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_DISKCACHE_H__ +#define TALK_BASE_DISKCACHE_H__ + +#include <map> +#include <string> + +#ifdef WIN32 +#undef UnlockResource +#endif // WIN32 + +namespace talk_base { + +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// DiskCache - An LRU cache of streams, stored on disk. +// +// Streams are identified by a unique resource id. Multiple streams can be +// associated with each resource id, distinguished by an index. When old +// resources are flushed from the cache, all streams associated with those +// resources are removed together. +// DiskCache is designed to persist across executions of the program. It is +// safe for use from an arbitrary number of users on a single thread, but not +// from multiple threads or other processes. +/////////////////////////////////////////////////////////////////////////////// + +class DiskCache { +public: + DiskCache(); + ~DiskCache(); + + bool Initialize(const std::string& folder, size_t size); + bool Purge(); + + bool LockResource(const std::string& id); + StreamInterface* WriteResource(const std::string& id, size_t index); + bool UnlockResource(const std::string& id); + + StreamInterface* ReadResource(const std::string& id, size_t index) const; + + bool HasResource(const std::string& id) const; + bool HasResourceStream(const std::string& id, size_t index) const; + bool DeleteResource(const std::string& id); + + protected: + virtual bool InitializeEntries() = 0; + virtual bool PurgeFiles() = 0; + + virtual bool FileExists(const std::string& filename) const = 0; + virtual bool DeleteFile(const std::string& filename) const = 0; + + enum LockState { LS_UNLOCKED, LS_LOCKED, LS_UNLOCKING }; + struct Entry { + LockState lock_state; + mutable size_t accessors; + size_t size; + size_t streams; + time_t last_modified; + }; + typedef std::map<std::string, Entry> EntryMap; + friend class DiskCacheAdapter; + + bool CheckLimit(); + + std::string IdToFilename(const std::string& id, size_t index) const; + bool FilenameToId(const std::string& filename, std::string* id, + size_t* index) const; + + const Entry* GetEntry(const std::string& id) const { + return const_cast<DiskCache*>(this)->GetOrCreateEntry(id, false); + } + Entry* GetOrCreateEntry(const std::string& id, bool create); + + void ReleaseResource(const std::string& id, size_t index) const; + + std::string folder_; + size_t max_cache_, total_size_; + EntryMap map_; + mutable size_t total_accessors_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// CacheLock - Automatically manage locking and unlocking, with optional +// rollback semantics +/////////////////////////////////////////////////////////////////////////////// + +class CacheLock { +public: + CacheLock(DiskCache* cache, const std::string& id, bool rollback = false) + : cache_(cache), id_(id), rollback_(rollback) + { + locked_ = cache_->LockResource(id_); + } + ~CacheLock() { + if (locked_) { + cache_->UnlockResource(id_); + if (rollback_) { + cache_->DeleteResource(id_); + } + } + } + bool IsLocked() const { return locked_; } + void Commit() { rollback_ = false; } + +private: + DiskCache* cache_; + std::string id_; + bool rollback_, locked_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_DISKCACHE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc b/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc new file mode 100644 index 0000000..3132602 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcache_win32.cc @@ -0,0 +1,76 @@ +#include "talk/base/win32.h" +#include <shellapi.h> +#include <shlobj.h> +#include <tchar.h> + +#include <time.h> + +#include "talk/base/common.h" +#include "talk/base/diskcache.h" +#include "talk/base/pathutils.h" +#include "talk/base/stream.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" + +#include "talk/base/diskcache_win32.h" + +namespace talk_base { + +bool DiskCacheWin32::InitializeEntries() { + // Note: We could store the cache information in a separate file, for faster + // initialization. Figuring it out empirically works, too. + + std::wstring path16 = ToUtf16(folder_); + path16.append(1, '*'); + + WIN32_FIND_DATA find_data; + HANDLE find_handle = FindFirstFile(path16.c_str(), &find_data); + if (find_handle != INVALID_HANDLE_VALUE) { + do { + size_t index; + std::string id; + if (!FilenameToId(ToUtf8(find_data.cFileName), &id, &index)) + continue; + + Entry* entry = GetOrCreateEntry(id, true); + entry->size += find_data.nFileSizeLow; + total_size_ += find_data.nFileSizeLow; + entry->streams = max(entry->streams, index + 1); + FileTimeToUnixTime(find_data.ftLastWriteTime, &entry->last_modified); + + } while (FindNextFile(find_handle, &find_data)); + + FindClose(find_handle); + } + + return true; +} + +bool DiskCacheWin32::PurgeFiles() { + std::wstring path16 = ToUtf16(folder_); + path16.append(1, '*'); + path16.append(1, '\0'); + + SHFILEOPSTRUCT file_op = { 0 }; + file_op.wFunc = FO_DELETE; + file_op.pFrom = path16.c_str(); + file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT + | FOF_NORECURSION | FOF_FILESONLY; + if (0 != SHFileOperation(&file_op)) { + LOG_F(LS_ERROR) << "Couldn't delete cache files"; + return false; + } + + return true; +} + +bool DiskCacheWin32::FileExists(const std::string& filename) const { + DWORD result = ::GetFileAttributes(ToUtf16(filename).c_str()); + return (INVALID_FILE_ATTRIBUTES != result); +} + +bool DiskCacheWin32::DeleteFile(const std::string& filename) const { + return ::DeleteFile(ToUtf16(filename).c_str()) != 0; +} + +} diff --git a/Plugins/jingle/libjingle/talk/base/diskcache_win32.h b/Plugins/jingle/libjingle/talk/base/diskcache_win32.h new file mode 100644 index 0000000..aa5a1b6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcache_win32.h @@ -0,0 +1,28 @@ +// +// DiskCacheWin32.h +// Macshroom +// +// Created by Moishe Lettvin on 11/7/06. +// Copyright (C) 2006 Google Inc. All rights reserved. +// +// + +#ifndef TALK_BASE_DISKCACHEWIN32_H__ +#define TALK_BASE_DISKCACHEWIN32_H__ + +#include "talk/base/diskcache.h" + +namespace talk_base { + +class DiskCacheWin32 : public DiskCache { + protected: + virtual bool InitializeEntries(); + virtual bool PurgeFiles(); + + virtual bool FileExists(const std::string& filename) const; + virtual bool DeleteFile(const std::string& filename) const; +}; + +} + +#endif // TALK_BASE_DISKCACHEWIN32_H__ diff --git a/Plugins/jingle/libjingle/talk/base/diskcachestd.cc b/Plugins/jingle/libjingle/talk/base/diskcachestd.cc new file mode 100644 index 0000000..f50c7d7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcachestd.cc @@ -0,0 +1,30 @@ +// +// DiskCacheStd.cc +// Macshroom +// +// Created by Moishe Lettvin on 11/7/06. +// Copyright (C) 2006 Google Inc. All rights reserved. +// +// + +#include "diskcachestd.h" + +namespace talk_base { + +bool DiskCacheStd::InitializeEntries() { + return false; +} + +bool DiskCacheStd::PurgeFiles() { + return false; +} + +bool DiskCacheStd::FileExists(const std::string& filename) const { + return false; +} + +bool DiskCacheStd::DeleteFile(const std::string& filename) const { + return false; +} + +} diff --git a/Plugins/jingle/libjingle/talk/base/diskcachestd.h b/Plugins/jingle/libjingle/talk/base/diskcachestd.h new file mode 100644 index 0000000..b676771 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/diskcachestd.h @@ -0,0 +1,28 @@ +// +// DiskCacheStd.h +// Macshroom +// +// Created by Moishe Lettvin on 11/7/06. +// Copyright (C) 2006 Google Inc. All rights reserved. +// +// + +#ifndef TALK_BASE_DISKCACHESTD_H__ +#define TALK_BASE_DISKCACHESTD_H__ + +#include "talk/base/diskcache.h" + +namespace talk_base { + +class DiskCacheStd : public DiskCache { + protected: + virtual bool InitializeEntries(); + virtual bool PurgeFiles(); + + virtual bool FileExists(const std::string& filename) const; + virtual bool DeleteFile(const std::string& filename) const; +}; + +} + +#endif // TALK_BASE_DISKCACHESTD_H__ diff --git a/Plugins/jingle/libjingle/talk/base/event.h b/Plugins/jingle/libjingle/talk/base/event.h new file mode 100644 index 0000000..ca15c38 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/event.h @@ -0,0 +1,79 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_EVENT_H__ +#define TALK_BASE_EVENT_H__ + +namespace talk_base { + +#ifdef WIN32 +class Event { +public: + Event() { + event_ = CreateEvent(NULL, FALSE, FALSE, NULL); + } + ~Event() { + CloseHandle(event_); + } + void Set() { + SetEvent(event_); + } + void Reset() { + ResetEvent(event_); + } + void Wait() { + WaitForSingleObject(event_, INFINITE); + } +private: + HANDLE event_; +}; +#endif + +#ifdef POSIX +#include <cassert> +class Event { + Event() { + assert(false); + } + ~Event() { + assert(false); + } + void Set() { + assert(false); + } + void Reset() { + assert(false); + } + void Wait() { + assert(false); + } +}; +#endif + +} // namespace talk_base + +#endif // TALK_BASE_EVENT_H__ diff --git a/Plugins/jingle/libjingle/talk/base/fileutils.cc b/Plugins/jingle/libjingle/talk/base/fileutils.cc new file mode 100644 index 0000000..12c2576 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/fileutils.cc @@ -0,0 +1,273 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <errno.h> +#include <cassert> + +#ifdef WIN32 +#include "talk/base/convert.h" +#endif + +#include "talk/base/pathutils.h" +#include "talk/base/fileutils.h" +#include "talk/base/stringutils.h" +#include "talk/base/stream.h" + +#include "talk/base/unixfilesystem.h" +#include "talk/base/win32filesystem.h" + +#ifndef WIN32 +#define MAX_PATH 256 +#endif + +namespace talk_base { + +////////////////////////// +// Directory Iterator // +////////////////////////// + +// A Directoryraverser is created with a given directory. It originally points to +// the first file in the directory, and can be advanecd with Next(). This allows you +// to get information about each file. + + // Constructor +DirectoryIterator::DirectoryIterator() : +#ifdef _WIN32 + handle_(INVALID_HANDLE_VALUE) +#else + dir_(NULL), dirent_(NULL) +#endif +{} + + // Destructor +DirectoryIterator::~DirectoryIterator() { +#ifdef WIN32 + if (handle_ != INVALID_HANDLE_VALUE) + ::FindClose(handle_); +#else + if (dir_) + closedir(dir_); +#endif +} + + // Starts traversing a directory. + // dir is the directory to traverse + // returns true if the directory exists and is valid +bool DirectoryIterator::Iterate(const Pathname &dir) { + directory_ = dir.pathname(); +#ifdef WIN32 + if (handle_ != INVALID_HANDLE_VALUE) + ::FindClose(handle_); + std::string d = dir.pathname() + '*'; + handle_ = ::FindFirstFile(Utf16(d).AsWz(), &data_); + if (handle_ == INVALID_HANDLE_VALUE) + return false; +#else + if (dir_ != NULL) + closedir(dir_); + dir_ = ::opendir(directory_.c_str()); + if (dir_ == NULL) + return false; + dirent_ = readdir(dir_); + if (dirent_ == NULL) + return false; + + if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) + return false; +#endif + return true; +} + + // Advances to the next file + // returns true if there were more files in the directory. +bool DirectoryIterator::Next() { +#ifdef WIN32 + return ::FindNextFile(handle_, &data_) == TRUE; +#else + dirent_ = ::readdir(dir_); + if (dirent_ == NULL) + return false; + + return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; +#endif +} + + // returns true if the file currently pointed to is a directory +bool DirectoryIterator::IsDirectory() const { +#ifdef WIN32 + return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE; +#else + return S_ISDIR(stat_.st_mode); +#endif +} + + // returns the name of the file currently pointed to +std::string DirectoryIterator::Name() const { +#ifdef WIN32 + return Utf8(data_.cFileName).AsString(); +#else + assert(dirent_ != NULL); + return dirent_->d_name; +#endif +} + + // returns the size of the file currently pointed to +size_t DirectoryIterator::FileSize() const { +#ifndef WIN32 + return stat_.st_size; +#else + return data_.nFileSizeLow; +#endif +} + + // returns the last modified time of this file +time_t DirectoryIterator::FileModifyTime() const { +#ifdef WIN32 +return 0; +#else + return stat_.st_mtime; +#endif +} + +Filesystem *Filesystem::default_filesystem_ = 0; + + +bool Filesystem::CreateFolder(const Pathname &pathname) +{ + return EnsureDefaultFilesystem()->CreateFolderI(pathname); +} + +FileStream *Filesystem::OpenFile(const Pathname &filename, + const std::string &mode) +{ + return EnsureDefaultFilesystem()->OpenFileI(filename, mode); +} + +bool Filesystem::DeleteFile(const Pathname &filename) +{ + return EnsureDefaultFilesystem()->DeleteFileI(filename); +} + +bool Filesystem::MoveFile(const Pathname &old_path, const Pathname &new_path) +{ + return EnsureDefaultFilesystem()->MoveFileI(old_path, new_path); +} + +bool Filesystem::CopyFile(const Pathname &old_path, const Pathname &new_path) +{ + return EnsureDefaultFilesystem()->CopyFileI(old_path, new_path); +} + +bool Filesystem::IsFolder(const Pathname& pathname) +{ + return EnsureDefaultFilesystem()->IsFolderI(pathname); +} + +bool Filesystem::FileExists(const Pathname& pathname) +{ + return EnsureDefaultFilesystem()->FileExistsI(pathname); +} + +bool Filesystem::IsTemporaryPath(const Pathname& pathname) +{ + return EnsureDefaultFilesystem()->IsTemporaryPathI(pathname); +} + +bool Filesystem::GetTemporaryFolder(Pathname &path, bool create, + const std::string *append) +{ + return EnsureDefaultFilesystem()->GetTemporaryFolderI(path,create, append); +} + +std::string Filesystem::TempFilename(const Pathname &dir, const std::string &prefix) +{ + return EnsureDefaultFilesystem()->TempFilenameI(dir, prefix); +} + +bool Filesystem::GetFileSize(const Pathname &dir, size_t *size) +{ + return EnsureDefaultFilesystem()->GetFileSizeI(dir, size); +} + +Filesystem *Filesystem::EnsureDefaultFilesystem() +{ + if (!default_filesystem_) +#ifdef WIN32 + default_filesystem_ = new Win32Filesystem(); +#else + default_filesystem_ = new UnixFilesystem(); +#endif + return default_filesystem_; +} + +bool CreateUniqueFile(Pathname& path, bool create_empty) { + LOG(LS_INFO) << "Path " << path.pathname() << std::endl; + // If not folder is supplied, use the temporary folder + if (path.folder().empty()) { + Pathname temporary_path; + if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) { + printf("Get temp failed\n"); + return false; + } + path.SetFolder(temporary_path.pathname()); + } + + // If not filename is supplied, use a temporary name + if (path.filename().empty()) { + std::string folder(path.folder()); + std::string filename = Filesystem::TempFilename(folder, "gt"); + path.SetFilename(filename); + if (!create_empty) { + Filesystem::DeleteFile(path.pathname()); + } + return true; + } + + // Otherwise, create a unique name based on the given filename + // foo.txt -> foo-N.txt + const std::string basename = path.basename(); + const size_t MAX_VERSION = 100; + size_t version = 0; + while (version < MAX_VERSION) { + std::string pathname = path.pathname(); + + if (!Filesystem::FileExists(pathname)) { + if (create_empty) { + FileStream* fs = Filesystem::OpenFile(pathname,"w"); + delete fs; + } + return true; + } + version += 1; + char version_base[MAX_PATH]; + talk_base::sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u", + basename.c_str(), version); + path.SetBasename(version_base); + } + return true; +} +} diff --git a/Plugins/jingle/libjingle/talk/base/fileutils.h b/Plugins/jingle/libjingle/talk/base/fileutils.h new file mode 100644 index 0000000..868f4c2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/fileutils.h @@ -0,0 +1,185 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_FILEUTILS_H__ +#define TALK_BASE_FILEUTILS_H__ + +#include <string> + +#ifdef _WINDOWS +#include <windows.h> +#else +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#endif + +#include "talk/base/common.h" + +namespace talk_base { + +class FileStream; +class Pathname; + +////////////////////////// +// Directory Iterator // +////////////////////////// + +// A DirectoryTraverser is created with a given directory. It originally points to +// the first file in the directory, and can be advanecd with Next(). This allows you +// to get information about each file. + +class DirectoryIterator { + + public: + // Constructor + DirectoryIterator(); + + // Destructor + ~DirectoryIterator(); + + // Starts traversing a directory + // dir is the directory to traverse + // returns true if the directory exists and is valid + // The iterator will point to the first entry in the directory + bool Iterate(const Pathname &path); + + // Advances to the next file + // returns true if there were more files in the directory. + bool Next(); + + // returns true if the file currently pointed to is a directory + bool IsDirectory() const; + + // returns the name of the file currently pointed to + std::string Name() const; + + // returns the size of the file currently pointed to + size_t FileSize() const; + + // returns the last modified time of the file currently poitned to + time_t FileModifyTime() const; + + private: + std::string directory_; +#ifdef _WINDOWS + WIN32_FIND_DATA data_; + HANDLE handle_; +#else + DIR *dir_; + struct dirent *dirent_; + struct stat stat_; +#endif +}; + +class Filesystem { + public: + + virtual bool CreateFolderI(const Pathname &pathname) = 0; + + // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise, + // returns NULL. + virtual FileStream *OpenFileI(const Pathname &filename, + const std::string &mode) = 0; + + // This will attempt to delete the path located at filename. If filename is a file, + // it will be unlinked. If the path is a directory, it will recursively unlink and remove + // all the files and directory within it + virtual bool DeleteFileI(const Pathname &filename) = 0; + + // Creates a directory. This will call itself recursively to create /foo/bar even if + // /foo does not exist. + // Returns TRUE if function succeeds + + // This moves a file from old_path to new_path, where "file" can be a plain file + // or directory, which will be moved recursively. + // Returns true if function succeeds. + virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path) = 0; + + // This copies a file from old_path to _new_path where "file" can be a plain file + // or directory, which will be copied recursively. + // Returns true if function succeeds + virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path) = 0; + + // Returns true if a pathname is a directory + virtual bool IsFolderI(const Pathname& pathname) = 0; + + // Returns true if a file exists at this path + virtual bool FileExistsI(const Pathname& pathname) = 0; + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPathI(const Pathname& pathname) = 0; + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exists) + virtual bool GetTemporaryFolderI(Pathname &path, bool create, + const std::string *append) = 0; + + virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix) = 0; + + virtual bool GetFileSizeI(const Pathname &dir, size_t *size) = 0; + + static Filesystem *default_filesystem(void) { ASSERT(default_filesystem_!=NULL); return default_filesystem_; } + static void set_default_filesystem(Filesystem *filesystem) {default_filesystem_ = filesystem; } + + + static bool CreateFolder(const Pathname &pathname); + + static FileStream *OpenFile(const Pathname &filename, + const std::string &mode); + static bool DeleteFile(const Pathname &filename); + static bool MoveFile(const Pathname &old_path, const Pathname &new_path); + static bool CopyFile(const Pathname &old_path, const Pathname &new_path); + static bool IsFolder(const Pathname& pathname); + static bool FileExists(const Pathname &pathname); + static bool IsTemporaryPath(const Pathname& pathname); + static bool GetTemporaryFolder(Pathname &path, bool create, + const std::string *append); + static std::string TempFilename(const Pathname &dir, const std::string &prefix); + static bool GetFileSize(const Pathname &dir, size_t *size); + + private: + static Filesystem *default_filesystem_; + static Filesystem *EnsureDefaultFilesystem(); + +}; + +// Generates a unique temporary filename in 'directory' with the given 'prefix' + std::string TempFilename(const Pathname &dir, const std::string &prefix); + + // Generates a unique filename based on the input path. If no path component + // is specified, it uses the temporary directory. If a filename is provided, + // up to 100 variations of form basename-N.extension are tried. When + // create_empty is true, an empty file of this name is created (which + // decreases the chance of a temporary filename collision with another + // process). + bool CreateUniqueFile(talk_base::Pathname& path, bool create_empty); + +} + +#endif // TALK_BASE_FILEUTILS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc new file mode 100644 index 0000000..49db8a8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.cc @@ -0,0 +1,213 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <cassert> +#include <algorithm> +#ifdef OSX +#include <errno.h> +#endif + +#include "talk/base/firewallsocketserver.h" +#include "talk/base/asyncsocket.h" +#include "talk/base/logging.h" + +namespace talk_base { + +class FirewallSocket : public AsyncSocketAdapter { +public: + FirewallSocket(FirewallSocketServer * server, AsyncSocket * socket, int type) + : AsyncSocketAdapter(socket), server_(server), type_(type) { + } + FirewallSocket(FirewallSocketServer * server, Socket * socket, int type) + : AsyncSocketAdapter(socket), server_(server), type_(type) { + } + + virtual int Connect(const SocketAddress& addr) { + if (type_ == SOCK_STREAM) { + if (!server_->Check(FP_TCP, FD_OUT, addr)) { + //LOG(INFO) << "FirewallSocket::Connect - Outbound TCP connection denied"; + // Note: handle this asynchronously? + SetError(EHOSTUNREACH); + return SOCKET_ERROR; + } + } + return AsyncSocketAdapter::Connect(addr); + } + virtual int Send(const void * pv, size_t cb) { + if (type_ == SOCK_DGRAM) { + if (!server_->Check(FP_UDP, FD_OUT, GetRemoteAddress())) { + //LOG(INFO) << "FirewallSocket::Send - Outbound UDP packet dropped"; + return static_cast<int>(cb); + } + } + return AsyncSocketAdapter::Send(pv, cb); + } + virtual int SendTo(const void * pv, size_t cb, const SocketAddress& addr) { + if (type_ == SOCK_DGRAM) { + if (!server_->Check(FP_UDP, FD_OUT, addr)) { + //LOG(INFO) << "FirewallSocket::SendTo - Outbound UDP packet dropped"; + return static_cast<int>(cb); + } + } + return AsyncSocketAdapter::SendTo(pv, cb, addr); + } + virtual int Recv(void * pv, size_t cb) { + if (type_ == SOCK_DGRAM) { + if (!server_->Check(FP_UDP, FD_IN, GetRemoteAddress())) { + while (true) { + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res <= 0) + return res; + //LOG(INFO) << "FirewallSocket::Recv - Inbound UDP packet dropped"; + } + } + } + return AsyncSocketAdapter::Recv(pv, cb); + } + virtual int RecvFrom(void * pv, size_t cb, SocketAddress * paddr) { + if (type_ == SOCK_DGRAM) { + while (true) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res <= 0) + return res; + if (server_->Check(FP_UDP, FD_IN, *paddr)) + return res; + //LOG(INFO) << "FirewallSocket::RecvFrom - Inbound UDP packet dropped"; + } + } + return AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + } + virtual Socket * Accept(SocketAddress *paddr) { + while (Socket * sock = AsyncSocketAdapter::Accept(paddr)) { + if (server_->Check(FP_TCP, FD_IN, *paddr)) + return sock; + sock->Close(); + delete sock; + //LOG(INFO) << "FirewallSocket::Accept - Inbound TCP connection denied"; + } + return 0; + } + +private: + FirewallSocketServer * server_; + int type_; +}; + +FirewallSocketServer::FirewallSocketServer(SocketServer * server, FirewallManager * manager) : server_(server), manager_(manager) { + if (manager_) + manager_->AddServer(this); +} + +FirewallSocketServer::~FirewallSocketServer() { + if (manager_) + manager_->RemoveServer(this); +} + +void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) { + Rule r; + r.allow = allow; + r.p = p; + r.d = d; + r.addr = addr; + CritScope scope(&crit_); + rules_.push_back(r); +} + +void FirewallSocketServer::ClearRules() { + CritScope scope(&crit_); + rules_.clear(); +} + +bool FirewallSocketServer::Check(FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) { + CritScope scope(&crit_); + for (size_t i=0; i<rules_.size(); ++i) { + const Rule& r = rules_[i]; + if ((r.p != p) && (r.p != FP_ANY)) + continue; + if ((r.d != d) && (r.d != FD_ANY)) + continue; + if ((r.addr.ip() != addr.ip()) && !r.addr.IsAny()) + continue; + if ((r.addr.port() != addr.port()) && (r.addr.port() != 0)) + continue; + return r.allow; + } + return true; +} + +Socket* FirewallSocketServer::CreateSocket(int type) { + return WrapSocket(server_->CreateSocket(type), type); +} + +AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) { + return WrapSocket(server_->CreateAsyncSocket(type), type); +} + +Socket * FirewallSocketServer::WrapSocket(Socket * sock, int type) { + if (!sock) + return NULL; + return new FirewallSocket(this, sock, type); +} + +AsyncSocket * FirewallSocketServer::WrapSocket(AsyncSocket * sock, int type) { + if (!sock) + return NULL; + return new FirewallSocket(this, sock, type); +} + +FirewallManager::FirewallManager() { +} + +FirewallManager::~FirewallManager() { + assert(servers_.empty()); +} + +void FirewallManager::AddServer(FirewallSocketServer * server) { + CritScope scope(&crit_); + servers_.push_back(server); +} + +void FirewallManager::RemoveServer(FirewallSocketServer * server) { + CritScope scope(&crit_); + servers_.erase(std::remove(servers_.begin(), servers_.end(), server), servers_.end()); +} + +void FirewallManager::AddRule(bool allow, FirewallProtocol p, FirewallDirection d, const SocketAddress& addr) { + CritScope scope(&crit_); + for (std::vector<FirewallSocketServer *>::const_iterator it = servers_.begin(); it != servers_.end(); ++it) { + (*it)->AddRule(allow, p, d, addr); + } +} + +void FirewallManager::ClearRules() { + CritScope scope(&crit_); + for (std::vector<FirewallSocketServer *>::const_iterator it = servers_.begin(); it != servers_.end(); ++it) { + (*it)->ClearRules(); + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h new file mode 100644 index 0000000..10c07e6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/firewallsocketserver.h @@ -0,0 +1,95 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_FIREWALLSOCKETSERVER_H__ +#define TALK_BASE_FIREWALLSOCKETSERVER_H__ + +#include <vector> +#include "talk/base/socketserver.h" +#include "talk/base/criticalsection.h" + +namespace talk_base { + +class FirewallManager; + +// This SocketServer shim simulates a rule-based firewall server + +enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY }; +enum FirewallDirection { FD_IN, FD_OUT, FD_ANY }; + +class FirewallSocketServer : public SocketServer { +public: + FirewallSocketServer(SocketServer * server, FirewallManager * manager = 0); + virtual ~FirewallSocketServer(); + + void AddRule(bool allow, FirewallProtocol p = FP_ANY, FirewallDirection d = FD_ANY, const SocketAddress& addr = SocketAddress()); + void ClearRules(); + + bool Check(FirewallProtocol p, FirewallDirection d, const SocketAddress& addr); + + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual bool Wait(int cms, bool process_io) { return server_->Wait(cms, process_io); } + virtual void WakeUp() { return server_->WakeUp(); } + + Socket * WrapSocket(Socket * sock, int type); + AsyncSocket * WrapSocket(AsyncSocket * sock, int type); + +private: + SocketServer * server_; + FirewallManager * manager_; + CriticalSection crit_; + struct Rule { + bool allow; + FirewallProtocol p; + FirewallDirection d; + SocketAddress addr; + }; + std::vector<Rule> rules_; +}; + +// FirewallManager allows you to manage firewalls in multiple threads together + +class FirewallManager { +public: + FirewallManager(); + ~FirewallManager(); + + void AddServer(FirewallSocketServer * server); + void RemoveServer(FirewallSocketServer * server); + + void AddRule(bool allow, FirewallProtocol p = FP_ANY, FirewallDirection d = FD_ANY, const SocketAddress& addr = SocketAddress()); + void ClearRules(); + +private: + CriticalSection crit_; + std::vector<FirewallSocketServer *> servers_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_FIREWALLSOCKETSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/helpers.cc b/Plugins/jingle/libjingle/talk/base/helpers.cc new file mode 100644 index 0000000..627edce --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/helpers.cc @@ -0,0 +1,148 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/helpers.h" +#include "talk/base/time.h" +#include <cstdlib> +#include <cassert> + +// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will +// give cryptographically random values on all platforms. + +#ifdef WIN32 +#include <time.h> +#include <windows.h> +#endif + +namespace cricket { + +static long g_seed = 1L; + +int GetRandom() { + return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff; +} + +static bool s_initrandom; + +long GetRandomSeed() { + return g_seed; +} + +void SetRandomSeed(unsigned long seed) +{ + s_initrandom = true; + g_seed = (long)seed; +} + +void InitRandom(const char *client_unique, size_t len) { + // Hash this string - unique per client + + uint32 hash = 0; + if (client_unique != NULL) { + for (int i = 0; i < (int)len; i++) + hash = ((hash << 2) + hash) + client_unique[i]; + } + + // Now initialize the seed against a high resolution + // counter + + unsigned long seed = GetRandomSeed(); + +#ifdef WIN32 + bool success = false; + HCRYPTPROV hProv = NULL; + if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + success = (FALSE != CryptGenRandom(hProv, + sizeof(seed), + reinterpret_cast<BYTE*>(&seed))); + CryptReleaseContext(hProv, 0); + } + + if (!success) { + LARGE_INTEGER big; + QueryPerformanceCounter(&big); + seed = big.LowPart; + } +#else + seed = talk_base::Time(); +#endif + + SetRandomSeed(seed ^ hash); +} + +const char BASE64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +// Generates a random string of the given length. We generate base64 values so +// that they will be printable, though that's not necessary. + +std::string CreateRandomString(int len) { + // Random number generator should of been initialized! + assert(s_initrandom); + if (!s_initrandom) + InitRandom(0, 0); + + std::string str; + for (int i = 0; i < len; i++) +#if defined(_MSC_VER) && _MSC_VER < 1300 + str.insert(str.end(), BASE64[GetRandom() & 63]); +#else + str.push_back(BASE64[GetRandom() & 63]); +#endif + return str; +} + +uint32 CreateRandomId() { + uint8 b1 = (uint8)(GetRandom() & 255); + uint8 b2 = (uint8)(GetRandom() & 255); + uint8 b3 = (uint8)(GetRandom() & 255); + uint8 b4 = (uint8)(GetRandom() & 255); + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +bool IsBase64Char(char ch) { + return (('A' <= ch) && (ch <= 'Z')) || + (('a' <= ch) && (ch <= 'z')) || + (('0' <= ch) && (ch <= '9')) || + (ch == '+') || (ch == '/'); +} + +bool IsBase64Encoded(const std::string& str) { + for (size_t i = 0; i < str.size(); ++i) { + if (!IsBase64Char(str.at(i))) + return false; + } + return true; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/base/helpers.h b/Plugins/jingle/libjingle/talk/base/helpers.h new file mode 100644 index 0000000..696edbe --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/helpers.h @@ -0,0 +1,55 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __HELPERS_H__ +#define __HELPERS_H__ + +#include "talk/base/basictypes.h" +#include <string> + +namespace cricket { + +// srand initializer +void InitRandom(const char *client_unique, size_t len); + +// For testing, the random seed can be directly accessed. +long GetRandomSeed(); +void SetRandomSeed(unsigned long seed); + +// Generates a (cryptographically) random string of the given length. +std::string CreateRandomString(int length); + +// Generates a random id +uint32 CreateRandomId(); + +// Determines whether the given string consists entirely of valid base64 +// encoded characters. +bool IsBase64Encoded(const std::string& str); + +} // namespace cricket + +#endif // __HELPERS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/host.cc b/Plugins/jingle/libjingle/talk/base/host.cc new file mode 100644 index 0000000..df5f7f7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/host.cc @@ -0,0 +1,100 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <string> +#include <iostream> +#include <cassert> +#include <errno.h> + +#ifdef POSIX +extern "C" { +#include <sys/utsname.h> +} +#endif // POSIX + +#include "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; + using ::exit; +} +#endif + +namespace talk_base { + +namespace { + +void FatalError(const std::string& name, int err) { + PLOG(LERROR, err) << name; + std::exit(1); +} + +} + +#ifdef POSIX +std::string GetHostName() { + struct utsname nm; + if (uname(&nm) < 0) + FatalError("uname", errno); + return std::string(nm.nodename); +} +#endif + +#ifdef WIN32 +std::string GetHostName() { + // TODO: fix this + return "cricket"; +} +#endif + +// Records information about the local host. +Host* gLocalHost = 0; + +const Host& LocalHost() { + if (!gLocalHost) { + std::vector<Network*>* networks = new std::vector<Network*>; + NetworkManager::CreateNetworks(*networks); +#ifdef WIN32 + // This is sort of problematic... one part of the code (the unittests) wants + // 127.0.0.1 to be present and another part (port allocators) don't. Right + // now, they use different APIs, so we can have different behavior. But + // there is something wrong with this. + networks->push_back(new Network("localhost", + SocketAddress::StringToIP("127.0.0.1"))); +#endif + gLocalHost = new Host(GetHostName(), networks); + assert(gLocalHost->networks().size() > 0); + } + + return *gLocalHost; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/host.h b/Plugins/jingle/libjingle/talk/base/host.h new file mode 100644 index 0000000..7ee603d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/host.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HOST_H__ +#define TALK_BASE_HOST_H__ + +#include <string> +#include <vector> +#include "talk/base/network.h" + +namespace talk_base { + +// Provides information about a host in the network. +class Host { +public: + Host(const std::string& name, std::vector<Network*>* networks) + : name_(name), networks_(networks) { } + + const std::string& name() const { return name_; } + const std::vector<Network*>& networks() const { return *networks_; } + +private: + std::string name_; + std::vector<Network*>* networks_; +}; + +// Returns a reference to the description of the local host. +const Host& LocalHost(); + +// Returns the name of the local host. +std::string GetHostName(); + +} // namespace talk_base + +#endif // TALK_BASE_HOST_H__ diff --git a/Plugins/jingle/libjingle/talk/base/httpbase.cc b/Plugins/jingle/libjingle/talk/base/httpbase.cc new file mode 100644 index 0000000..06b7378 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpbase.cc @@ -0,0 +1,591 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef OSX +#include <errno.h> +#endif + +#ifdef WIN32 +#include "talk/base/win32.h" +#else // !WIN32 +#define SEC_E_CERT_EXPIRED (-2146893016) +#endif // !WIN32 + +#include "talk/base/common.h" +#include "talk/base/httpbase.h" +#include "talk/base/logging.h" +#include "talk/base/socket.h" +#include "talk/base/stringutils.h" + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// Helpers +////////////////////////////////////////////////////////////////////// + +bool MatchHeader(const char* str, size_t len, HttpHeader header) { + const char* const header_str = ToString(header); + const size_t header_len = strlen(header_str); + return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0); +} + +////////////////////////////////////////////////////////////////////// +// HttpParser +////////////////////////////////////////////////////////////////////// + +HttpParser::HttpParser() { + reset(); +} + +HttpParser::~HttpParser() { +} + +void +HttpParser::reset() { + state_ = ST_LEADER; + chunked_ = false; + data_size_ = SIZE_UNKNOWN; +} + +bool +HttpParser::process(const char* buffer, size_t len, size_t& processed, + HttpError& err) { + processed = 0; + err = HE_NONE; + + if (state_ >= ST_COMPLETE) { + ASSERT(false); + return false; + } + + while (true) { + if (state_ < ST_DATA) { + size_t pos = processed; + while ((pos < len) && (buffer[pos] != '\n')) { + pos += 1; + } + if (pos >= len) { + break; // don't have a full header + } + const char* line = buffer + processed; + size_t len = (pos - processed); + processed = pos + 1; + while ((len > 0) && isspace(static_cast<unsigned char>(line[len-1]))) { + len -= 1; + } + if (!process_line(line, len, err)) { + return false; // no more processing + } + } else if (data_size_ == 0) { + if (chunked_) { + state_ = ST_CHUNKTERM; + } else { + return false; + } + } else { + size_t available = len - processed; + if (available <= 0) { + break; // no more data + } + if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) { + available = data_size_; + } + size_t read = 0; + err = onHttpRecvData(buffer + processed, available, read); + if (err != HE_NONE) { + return false; // error occurred + } + processed += read; + if (data_size_ != SIZE_UNKNOWN) { + data_size_ -= read; + } + } + } + + return true; +} + +bool +HttpParser::process_line(const char* line, size_t len, HttpError& err) { + switch (state_) { + case ST_LEADER: + state_ = ST_HEADERS; + err = onHttpRecvLeader(line, len); + break; + + case ST_HEADERS: + if (len > 0) { + const char* value = strchrn(line, len, ':'); + if (!value) { + err = HE_PROTOCOL; + break; + } + size_t nlen = (value - line); + const char* eol = line + len; + do { + value += 1; + } while ((value < eol) && isspace(static_cast<unsigned char>(*value))); + size_t vlen = eol - value; + if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) { + if (sscanf(value, "%d", &data_size_) != 1) { + err = HE_PROTOCOL; + break; + } + } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) { + if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) { + chunked_ = true; + } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) { + chunked_ = false; + } else { + err = HE_PROTOCOL; + break; + } + } + err = onHttpRecvHeader(line, nlen, value, vlen); + } else { + state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; + err = onHttpRecvHeaderComplete(chunked_, data_size_); + } + break; + + case ST_CHUNKSIZE: + if (len > 0) { + char* ptr = NULL; + data_size_ = strtoul(line, &ptr, 16); + if (ptr != line + len) { + err = HE_PROTOCOL; + break; + } + state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA; + } else { + err = HE_PROTOCOL; + } + break; + + case ST_CHUNKTERM: + if (len > 0) { + err = HE_PROTOCOL; + } else { + state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA; + } + break; + + case ST_TRAILERS: + if (len == 0) { + return false; + } + // err = onHttpRecvTrailer(); + break; + + default: + break; + } + + return (err == HE_NONE); +} + +void +HttpParser::end_of_input() { + if ((state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN)) { + complete(HE_NONE); + } else { + complete(HE_DISCONNECTED); + } +} + +void +HttpParser::complete(HttpError err) { + if (state_ < ST_COMPLETE) { + state_ = ST_COMPLETE; + onHttpRecvComplete(err); + } +} + +////////////////////////////////////////////////////////////////////// +// HttpBase +////////////////////////////////////////////////////////////////////// + +HttpBase::HttpBase() : mode_(HM_NONE), data_(NULL), notify_(NULL), + stream_(NULL) { +} + +HttpBase::~HttpBase() { +} + +bool +HttpBase::isConnected() const { + return (stream_ != NULL) && (stream_->GetState() == SS_OPEN); +} + +bool +HttpBase::attach(StreamInterface* stream) { + if ((mode_ != HM_NONE) || (stream_ != NULL) || (stream == NULL)) { + ASSERT(false); + return false; + } + stream_ = stream; + stream_->SignalEvent.connect(this, &HttpBase::OnEvent); + mode_ = (stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE; + return true; +} + +StreamInterface* +HttpBase::detach() { + if (mode_ != HM_NONE) { + ASSERT(false); + return NULL; + } + StreamInterface* stream = stream_; + stream_ = NULL; + if (stream) { + stream->SignalEvent.disconnect(this); + } + return stream; +} + +/* +bool +HttpBase::accept(PNSocket& socket) { + if (mode_ != HM_NONE) { + ASSERT(false); + return false; + } + + return socket.accept(stream_); +} + +void +HttpBase::connect(const SocketAddress& addr) { + if (mode_ != HM_NONE) { + ASSERT(false); + return; + } + + mode_ = HM_CONNECT; + + SocketAddress local; + if (!stream_.connect(local, addr) && !stream_.isBlocking()) { + onSocketConnect(&stream_, stream_.getError()); + } +} +*/ +void +HttpBase::send(HttpData* data) { + if (mode_ != HM_NONE) { + ASSERT(false); + return; + } else if (!isConnected()) { + OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED); + return; + } + + mode_ = HM_SEND; + data_ = data; + len_ = 0; + ignore_data_ = chunk_data_ = false; + + std::string encoding; + if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding) + && (encoding == "chunked")) { + chunk_data_ = true; + } + + len_ = data_->formatLeader(buffer_, sizeof(buffer_)); + len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n"); + header_ = data_->begin(); + queue_headers(); + + OnEvent(stream_, SE_WRITE, 0); +} + +void +HttpBase::recv(HttpData* data) { + if (mode_ != HM_NONE) { + ASSERT(false); + return; + } else if (!isConnected()) { + OnEvent(stream_, SE_CLOSE, HE_DISCONNECTED); + return; + } + + mode_ = HM_RECV; + data_ = data; + len_ = 0; + ignore_data_ = chunk_data_ = false; + + reset(); + OnEvent(stream_, SE_READ, 0); +} + +void +HttpBase::abort(HttpError err) { + if (mode_ != HM_NONE) { + if (stream_ != NULL) { + stream_->Close(); + } + do_complete(err); + } +} + +void +HttpBase::flush_data() { + while (true) { + for (size_t start = 0; start < len_; ) { + size_t written; + int error; + StreamResult result = stream_->Write(buffer_ + start, len_ - start, + &written, &error); + if (result == SR_SUCCESS) { + //LOG_F(LS_INFO) << "wrote " << res << " bytes"; + start += written; + continue; + } else if (result == SR_BLOCK) { + //LOG_F(LS_INFO) << "blocking"; + len_ -= start; + memmove(buffer_, buffer_ + start, len_); + return; + } else { + ASSERT(result == SR_ERROR); + LOG_F(LS_ERROR) << "error"; + OnEvent(stream_, SE_CLOSE, error); + return; + } + } + len_ = 0; + + // Check for more headers + if (header_ != data_->end()) { + queue_headers(); + continue; + } + + // Check for document data + if (!data_->document.get()) + break; + + size_t offset = 0, reserve = 0; + if (chunk_data_) { + // Reserve 10 characters at the start for 8-byte hex value and \r\n + offset = 10; + // ... and 2 characters at the end for \r\n + reserve = offset + 2; + ASSERT(reserve < sizeof(buffer_)); + } + + int error = 0; + StreamResult result = data_->document->Read(buffer_ + offset, + sizeof(buffer_) - reserve, + &len_, &error); + if (result == SR_SUCCESS) { + if (!chunk_data_) + continue; + + // Prepend the length and append \r\n + sprintfn(buffer_, offset, "%.*x", (offset - 2), len_); + memcpy(buffer_ + offset - 2, "\r\n", 2); + memcpy(buffer_ + offset + len_, "\r\n", 2); + ASSERT(len_ + reserve <= sizeof(buffer_)); + len_ += reserve; + } else if (result == SR_EOS) { + if (!chunk_data_) + break; + + // Append the empty chunk and empty trailers, then turn off chunking. + len_ = sprintfn(buffer_, sizeof(buffer_), "0\r\n\r\n"); + chunk_data_ = false; + } else { + LOG_F(LS_ERROR) << "Read error: " << error; + do_complete(HE_STREAM); + return; + } + } + + do_complete(); +} + +void +HttpBase::queue_headers() { + while (header_ != data_->end()) { + size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_, + "%.*s: %.*s\r\n", + header_->first.size(), header_->first.data(), + header_->second.size(), header_->second.data()); + if (len_ + len < sizeof(buffer_) - 3) { + len_ += len; + ++header_; + } else if (len_ == 0) { + LOG(WARNING) << "discarding header that is too long: " << header_->first; + ++header_; + } else { + break; + } + } + if (header_ == data_->end()) { + len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n"); + } +} + +void +HttpBase::do_complete(HttpError err) { + ASSERT(mode_ != HM_NONE); + HttpMode mode = mode_; + mode_ = HM_NONE; + data_ = NULL; + if (notify_) { + notify_->onHttpComplete(mode, err); + } +} + +void +HttpBase::OnEvent(StreamInterface* stream, int events, int error) { + if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) { + do_complete(); + return; + } + + if ((events & SE_WRITE) && (mode_ == HM_SEND)) { + flush_data(); + return; + } + + if ((events & SE_READ) && (mode_ == HM_RECV)) { + // Do to the latency between receiving read notifications from + // pseudotcpchannel, we rely on repeated calls to read in order to acheive + // ideal throughput. The number of reads is limited to prevent starving + // the caller. + size_t loop_count = 0; + const size_t kMaxReadCount = 20; + while (true) { + if (len_ >= sizeof(buffer_)) { + do_complete(HE_OVERFLOW); + return; + } + size_t read; + int error; + StreamResult result = stream_->Read(buffer_ + len_, + sizeof(buffer_) - len_, + &read, &error); + if ((result == SR_BLOCK) || (result == SR_EOS)) + return; + if (result == SR_ERROR) { + OnEvent(stream_, SE_CLOSE, error); + return; + } + ASSERT(result == SR_SUCCESS); + //LOG(INFO) << "HttpBase << " << std::string(buffer_ + len_, res); + len_ += read; + HttpError herr; + bool more = process(buffer_, len_, read, herr); + len_ -= read; + memcpy(buffer_, buffer_ + read, len_); + if (!more) { + complete(herr); + return; + } + if (++loop_count > kMaxReadCount) { + LOG_F(LS_WARNING) << "danger of starvation"; + break; + } + } + return; + } + + if ((events & SE_CLOSE) == 0) + return; + + if (stream_ != NULL) { + stream_->Close(); + } + HttpError herr; + // TODO: Pass through errors instead of translating them? + if (error == 0) { + herr = HE_DISCONNECTED; + } else if (error == SOCKET_EACCES) { + herr = HE_AUTH; + } else if (error == SEC_E_CERT_EXPIRED) { + herr = HE_CERTIFICATE_EXPIRED; + } else { + LOG_F(LS_ERROR) << "SE_CLOSE error: " << error; + herr = HE_SOCKET; + } + if ((mode_ == HM_RECV) && (error == HE_NONE)) { + end_of_input(); + } else if (mode_ != HM_NONE) { + do_complete(mkerr(herr, HE_DISCONNECTED)); + } else if (notify_) { + notify_->onHttpClosed(mkerr(herr, HE_DISCONNECTED)); + } +} + +// +// HttpParser Implementation +// + +HttpError +HttpBase::onHttpRecvLeader(const char* line, size_t len) { + return data_->parseLeader(line, len); +} + +HttpError +HttpBase::onHttpRecvHeader(const char* name, size_t nlen, const char* value, + size_t vlen) { + std::string sname(name, nlen), svalue(value, vlen); + data_->addHeader(sname, svalue); + //LOG(INFO) << sname << ": " << svalue; + return HE_NONE; +} + +HttpError +HttpBase::onHttpRecvHeaderComplete(bool chunked, size_t& data_size) { + return notify_ ? notify_->onHttpHeaderComplete(chunked, data_size) : HE_NONE; +} + +HttpError +HttpBase::onHttpRecvData(const char* data, size_t len, size_t& read) { + if (ignore_data_ || !data_->document.get()) { + read = len; + return HE_NONE; + } + int error = 0; + switch (data_->document->Write(data, len, &read, &error)) { + case SR_SUCCESS: + return HE_NONE; + case SR_EOS: + case SR_BLOCK: + LOG_F(LS_ERROR) << "Write EOS or block"; + return HE_STREAM; + } + LOG_F(LS_ERROR) << "Write error: " << error; + return HE_STREAM; +} + +void +HttpBase::onHttpRecvComplete(HttpError err) { + do_complete(err); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/httpbase.h b/Plugins/jingle/libjingle/talk/base/httpbase.h new file mode 100644 index 0000000..64aefe9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpbase.h @@ -0,0 +1,142 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HTTPBASE_H__ +#define TALK_BASE_HTTPBASE_H__ + +#include "talk/base/httpcommon.h" + +namespace talk_base { + +class StreamInterface; + +////////////////////////////////////////////////////////////////////// +// HttpParser +////////////////////////////////////////////////////////////////////// + +class HttpParser { +public: + HttpParser(); + virtual ~HttpParser(); + + void reset(); + bool process(const char* buffer, size_t len, size_t& read, HttpError& err); + void end_of_input(); + void complete(HttpError err); + +protected: + bool process_line(const char* line, size_t len, HttpError& err); + + // HttpParser Interface + virtual HttpError onHttpRecvLeader(const char* line, size_t len) = 0; + virtual HttpError onHttpRecvHeader(const char* name, size_t nlen, + const char* value, size_t vlen) = 0; + virtual HttpError onHttpRecvHeaderComplete(bool chunked, size_t& data_size) = 0; + virtual HttpError onHttpRecvData(const char* data, size_t len, size_t& read) = 0; + virtual void onHttpRecvComplete(HttpError err) = 0; + +private: + enum State { + ST_LEADER, ST_HEADERS, + ST_CHUNKSIZE, ST_CHUNKTERM, ST_TRAILERS, + ST_DATA, ST_COMPLETE + } state_; + bool chunked_; + size_t data_size_; +}; + +////////////////////////////////////////////////////////////////////// +// IHttpNotify +////////////////////////////////////////////////////////////////////// + +enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND }; + +class IHttpNotify { +public: + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0; + virtual void onHttpComplete(HttpMode mode, HttpError err) = 0; + virtual void onHttpClosed(HttpError err) = 0; +}; + +////////////////////////////////////////////////////////////////////// +// HttpBase +////////////////////////////////////////////////////////////////////// + +class HttpBase : private HttpParser, public sigslot::has_slots<> { +public: + HttpBase(); + virtual ~HttpBase(); + + void notify(IHttpNotify* notify) { notify_ = notify; } + bool attach(StreamInterface* stream); + StreamInterface* stream() { return stream_; } + StreamInterface* detach(); + bool isConnected() const; + + void send(HttpData* data); + void recv(HttpData* data); + void abort(HttpError err); + + HttpMode mode() const { return mode_; } + + void set_ignore_data(bool ignore) { ignore_data_ = ignore; } + bool ignore_data() const { return ignore_data_; } + +protected: + void flush_data(); + void queue_headers(); + void do_complete(HttpError err = HE_NONE); + + void OnEvent(StreamInterface* stream, int events, int error); + + // HttpParser Interface + virtual HttpError onHttpRecvLeader(const char* line, size_t len); + virtual HttpError onHttpRecvHeader(const char* name, size_t nlen, + const char* value, size_t vlen); + virtual HttpError onHttpRecvHeaderComplete(bool chunked, size_t& data_size); + virtual HttpError onHttpRecvData(const char* data, size_t len, size_t& read); + virtual void onHttpRecvComplete(HttpError err); + +private: + enum { kBufferSize = 32 * 1024 }; + + HttpMode mode_; + HttpData* data_; + IHttpNotify* notify_; + StreamInterface* stream_; + char buffer_[kBufferSize]; + size_t len_; + + bool ignore_data_, chunk_data_; + HttpData::const_iterator header_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_HTTPBASE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/httpclient.cc b/Plugins/jingle/libjingle/talk/base/httpclient.cc new file mode 100644 index 0000000..49c5070 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpclient.cc @@ -0,0 +1,714 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <time.h> + +#include "talk/base/httpcommon-inl.h" + +#include "talk/base/asyncsocket.h" +#include "talk/base/common.h" +#include "talk/base/diskcache.h" +#include "talk/base/httpclient.h" +#include "talk/base/logging.h" +#include "talk/base/pathutils.h" +#include "talk/base/socketstream.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" +#include "talk/base/basicdefs.h" + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// Helpers +////////////////////////////////////////////////////////////////////// + +namespace { + +const size_t kCacheHeader = 0; +const size_t kCacheBody = 1; + +std::string HttpAddress(const SocketAddress& address) { + return (address.port() == HTTP_DEFAULT_PORT) + ? address.hostname() : address.ToString(); +} + +// Convert decimal string to integer +bool HttpStringToInt(const std::string& str, unsigned long* val) { + ASSERT(NULL != val); + char* eos = NULL; + *val = strtoul(str.c_str(), &eos, 10); + return (*eos == '\0'); +} + +bool HttpShouldCache(const HttpRequestData& request, + const HttpResponseData& response) { + bool verb_allows_cache = (request.verb == HV_GET) + || (request.verb == HV_HEAD); + bool is_range_response = response.hasHeader(HH_CONTENT_RANGE, NULL); + bool has_expires = response.hasHeader(HH_EXPIRES, NULL); + bool request_allows_cache = + has_expires || (std::string::npos != request.path.find('?')); + bool response_allows_cache = + has_expires || HttpCodeIsCacheable(response.scode); + + bool may_cache = verb_allows_cache + && request_allows_cache + && response_allows_cache + && !is_range_response; + + std::string value; + if (response.hasHeader(HH_CACHE_CONTROL, &value)) { + HttpAttributeList directives; + HttpParseAttributes(value.data(), value.size(), directives); + // Response Directives Summary: + // public - always cacheable + // private - do not cache in a shared cache + // no-cache - may cache, but must revalidate whether fresh or stale + // no-store - sensitive information, do not cache or store in any way + // max-age - supplants Expires for staleness + // s-maxage - use as max-age for shared caches, ignore otherwise + // must-revalidate - may cache, but must revalidate after stale + // proxy-revalidate - shared cache must revalidate + if (HttpHasAttribute(directives, "no-store", NULL)) { + may_cache = false; + } else if (HttpHasAttribute(directives, "public", NULL)) { + may_cache = true; + } + } + return may_cache; +} + +enum HttpCacheState { + HCS_FRESH, // In cache, may use + HCS_STALE, // In cache, must revalidate + HCS_NONE // Not in cache +}; + +HttpCacheState HttpGetCacheState(const HttpRequestData& request, + const HttpResponseData& response) { + // Temporaries + std::string s_temp; + unsigned long i_temp; + + // Current time + unsigned long now = time(0); + + HttpAttributeList cache_control; + if (response.hasHeader(HH_CACHE_CONTROL, &s_temp)) { + HttpParseAttributes(s_temp.data(), s_temp.size(), cache_control); + } + + // Compute age of cache document + unsigned long date; + if (!response.hasHeader(HH_DATE, &s_temp) + || !HttpDateToSeconds(s_temp, &date)) + return HCS_NONE; + + // TODO: Timestamp when cache request sent and response received? + unsigned long request_time = date; + unsigned long response_time = date; + + unsigned long apparent_age = 0; + if (response_time > date) { + apparent_age = response_time - date; + } + + unsigned long corrected_received_age = apparent_age; + if (response.hasHeader(HH_AGE, &s_temp) + && HttpStringToInt(s_temp, &i_temp)) { + corrected_received_age = stdmax(apparent_age, i_temp); + } + + unsigned long response_delay = response_time - request_time; + unsigned long corrected_initial_age = corrected_received_age + response_delay; + unsigned long resident_time = now - response_time; + unsigned long current_age = corrected_initial_age + resident_time; + + // Compute lifetime of document + unsigned long lifetime; + if (HttpHasAttribute(cache_control, "max-age", &s_temp)) { + lifetime = atoi(s_temp.c_str()); + } else if (response.hasHeader(HH_EXPIRES, &s_temp) + && HttpDateToSeconds(s_temp, &i_temp)) { + lifetime = i_temp - date; + } else if (response.hasHeader(HH_LAST_MODIFIED, &s_temp) + && HttpDateToSeconds(s_temp, &i_temp)) { + // TODO: Issue warning 113 if age > 24 hours + lifetime = (now - i_temp) / 10; + } else { + return HCS_STALE; + } + + return (lifetime > current_age) ? HCS_FRESH : HCS_STALE; +} + +enum HttpValidatorStrength { + HVS_NONE, + HVS_WEAK, + HVS_STRONG +}; + +HttpValidatorStrength +HttpRequestValidatorLevel(const HttpRequestData& request) { + if (HV_GET != request.verb) + return HVS_STRONG; + return request.hasHeader(HH_RANGE, NULL) ? HVS_STRONG : HVS_WEAK; +} + +HttpValidatorStrength +HttpResponseValidatorLevel(const HttpResponseData& response) { + std::string value; + if (response.hasHeader(HH_ETAG, &value)) { + bool is_weak = (strnicmp(value.c_str(), "W/", 2) == 0); + return is_weak ? HVS_WEAK : HVS_STRONG; + } + if (response.hasHeader(HH_LAST_MODIFIED, &value)) { + unsigned long last_modified, date; + if (HttpDateToSeconds(value, &last_modified) + && response.hasHeader(HH_DATE, &value) + && HttpDateToSeconds(value, &date) + && (last_modified + 60 < date)) { + return HVS_STRONG; + } + return HVS_WEAK; + } + return HVS_NONE; +} + +std::string GetCacheID(const SocketAddress& server, + const HttpRequestData& request) { + std::string url; + url.append(ToString(request.verb)); + url.append("_"); + if ((_strnicmp(request.path.c_str(), "http://", 7) == 0) + || (_strnicmp(request.path.c_str(), "https://", 8) == 0)) { + url.append(request.path); + } else { + url.append("http://"); + url.append(HttpAddress(server)); + url.append(request.path); + } + return url; +} + +} // anonymous namespace + +////////////////////////////////////////////////////////////////////// +// HttpClient +////////////////////////////////////////////////////////////////////// + +HttpClient::HttpClient(const std::string& agent, StreamPool* pool) +: agent_(agent), pool_(pool), fail_redirect_(false), absolute_uri_(false), + cache_(NULL), cache_state_(CS_READY) +{ + base_.notify(this); +} + +HttpClient::~HttpClient() { + base_.notify(NULL); + base_.abort(HE_SHUTDOWN); + release(); +} + +void HttpClient::reset() { + server_.Clear(); + request_.clear(true); + response_.clear(true); + context_.reset(); + base_.abort(HE_OPERATION_CANCELLED); +} + +void HttpClient::set_server(const SocketAddress& address) { + server_ = address; + // Setting 'Host' here allows it to be overridden before starting the request, + // if necessary. + request_.setHeader(HH_HOST, HttpAddress(server_), true); +} + +void HttpClient::start() { + if (base_.mode() != HM_NONE) { + // call reset() to abort an in-progress request + ASSERT(false); + return; + } + + ASSERT(!IsCacheActive()); + + if (request_.hasHeader(HH_TRANSFER_ENCODING, NULL)) { + // Exact size must be known on the client. Instead of using chunked + // encoding, wrap data with auto-caching file or memory stream. + ASSERT(false); + return; + } + + // If no content has been specified, using length of 0. + request_.setHeader(HH_CONTENT_LENGTH, "0", false); + + request_.setHeader(HH_USER_AGENT, agent_, false); + request_.setHeader(HH_CONNECTION, "Keep-Alive", false); + if (_strnicmp(request_.path.c_str(), "http", 4) == 0) { + request_.setHeader(HH_PROXY_CONNECTION, "Keep-Alive", false); + } + + bool absolute_uri = absolute_uri_; + if (PROXY_HTTPS == proxy_.type) { + request().version = HVER_1_0; + // Proxies require canonical form + absolute_uri = true; + } + + // Convert to canonical form (if not already) + if (absolute_uri && (_strnicmp(request().path.c_str(), "http://", 7) != 0)) { + std::string canonical_path("http://"); + canonical_path.append(HttpAddress(server_)); + canonical_path.append(request().path); + request().path = canonical_path; + } + + if ((NULL != cache_) && CheckCache()) { + return; + } + + int stream_err; + StreamInterface* stream = pool_->RequestConnectedStream(server_, &stream_err); + if (stream == NULL) { + if (stream_err) + LOG(LS_ERROR) << "RequestConnectedStream returned: " << stream_err; + onHttpComplete(HM_CONNECT, (stream_err == 0) ? HE_NONE : HE_SOCKET); + } else { + base_.attach(stream); + if (stream->GetState() == SS_OPEN) { + base_.send(&request_); + } + } +} + +void HttpClient::prepare_get(const std::string& url) { + reset(); + Url<char> purl(url); + set_server(SocketAddress(purl.server(), purl.port(), false)); + request().verb = HV_GET; + request().path = purl.full_path(); +} + +void HttpClient::prepare_post(const std::string& url, + const std::string& content_type, + StreamInterface* request_doc) { + reset(); + Url<char> purl(url); + set_server(SocketAddress(purl.server(), purl.port(), false)); + request().verb = HV_POST; + request().path = purl.full_path(); + request().setContent(content_type, request_doc); +} + +void HttpClient::release() { + if (StreamInterface* stream = base_.detach()) { + pool_->ReturnConnectedStream(stream); + } +} + +bool HttpClient::BeginCacheFile() { + ASSERT(NULL != cache_); + ASSERT(CS_READY == cache_state_); + + std::string id = GetCacheID(server_, request_); + CacheLock lock(cache_, id, true); + if (!lock.IsLocked()) { + LOG_F(LS_WARNING) << "Couldn't lock cache"; + return false; + } + + if (HE_NONE != WriteCacheHeaders(id)) { + return false; + } + + scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheBody)); + if (!stream.get()) { + LOG_F(LS_ERROR) << "Couldn't open body cache"; + return false; + } + lock.Commit(); + + // Let's secretly replace the response document with Folgers Crystals, + // er, StreamTap, so that we can mirror the data to our cache. + StreamInterface* output = response_.document.release(); + if (!output) { + output = new NullStream; + } + StreamTap* tap = new StreamTap(output, stream.release()); + response_.document.reset(tap); + return true; +} + +HttpError HttpClient::WriteCacheHeaders(const std::string& id) { + scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheHeader)); + if (!stream.get()) { + LOG_F(LS_ERROR) << "Couldn't open header cache"; + return HE_CACHE; + } + + // Write all unknown and end-to-end headers to a cache file + for (HttpData::const_iterator it = response_.begin(); + it != response_.end(); ++it) { + HttpHeader header; + if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header)) + continue; + std::string formatted_header(it->first); + formatted_header.append(": "); + formatted_header.append(it->second); + formatted_header.append("\r\n"); + StreamResult result = stream->WriteAll(formatted_header.data(), + formatted_header.length(), + NULL, NULL); + if (SR_SUCCESS != result) { + LOG_F(LS_ERROR) << "Couldn't write header cache"; + return HE_CACHE; + } + } + + return HE_NONE; +} + +void HttpClient::CompleteCacheFile() { + // Restore previous response document + StreamTap* tap = static_cast<StreamTap*>(response_.document.release()); + response_.document.reset(tap->Detach()); + + int error; + StreamResult result = tap->GetTapResult(&error); + + // Delete the tap and cache stream (which completes cache unlock) + delete tap; + + if (SR_SUCCESS != result) { + LOG(LS_ERROR) << "Cache file error: " << error; + cache_->DeleteResource(GetCacheID(server_, request_)); + } +} + +bool HttpClient::CheckCache() { + ASSERT(NULL != cache_); + ASSERT(CS_READY == cache_state_); + + std::string id = GetCacheID(server_, request_); + if (!cache_->HasResource(id)) { + // No cache file available + return false; + } + + HttpError error = ReadCacheHeaders(id, true); + + if (HE_NONE == error) { + switch (HttpGetCacheState(request_, response_)) { + case HCS_FRESH: + // Cache content is good, read from cache + break; + case HCS_STALE: + // Cache content may be acceptable. Issue a validation request. + if (PrepareValidate()) { + return false; + } + // Couldn't validate, fall through. + case HCS_NONE: + // Cache content is not useable. Issue a regular request. + response_.clear(false); + return false; + } + } + + if (HE_NONE == error) { + error = ReadCacheBody(id); + cache_state_ = CS_READY; + } + + if (HE_CACHE == error) { + LOG_F(LS_WARNING) << "Cache failure, continuing with normal request"; + response_.clear(false); + return false; + } + + SignalHttpClientComplete(this, error); + return true; +} + +HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) { + scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheHeader)); + if (!stream.get()) { + return HE_CACHE; + } + + HttpData::HeaderCombine combine = + override ? HttpData::HC_REPLACE : HttpData::HC_AUTO; + + while (true) { + std::string formatted_header; + StreamResult result = stream->ReadLine(&formatted_header); + if (SR_EOS == result) + break; + + if (SR_SUCCESS != result) { + LOG_F(LS_ERROR) << "ReadLine error in cache headers"; + return HE_CACHE; + } + size_t end_of_name = formatted_header.find(':'); + if (std::string::npos == end_of_name) { + LOG_F(LS_WARNING) << "Malformed cache header"; + continue; + } + size_t start_of_value = end_of_name + 1; + size_t end_of_value = formatted_header.length(); + while ((start_of_value < end_of_value) + && isspace(formatted_header[start_of_value])) + ++start_of_value; + while ((start_of_value < end_of_value) + && isspace(formatted_header[end_of_value-1])) + --end_of_value; + size_t value_length = end_of_value - start_of_value; + + std::string name(formatted_header.substr(0, end_of_name)); + std::string value(formatted_header.substr(start_of_value, value_length)); + response_.changeHeader(name, value, combine); + } + + response_.scode = HC_OK; + return HE_NONE; +} + +HttpError HttpClient::ReadCacheBody(const std::string& id) { + cache_state_ = CS_READING; + + HttpError error = HE_NONE; + + size_t data_size; + scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheBody)); + if (!stream.get() || !stream->GetSize(&data_size)) { + LOG_F(LS_ERROR) << "Unavailable cache body"; + error = HE_CACHE; + } else { + error = OnHeaderAvailable(false, false, data_size); + } + + if ((HE_NONE == error) + && (HV_HEAD != request_.verb) + && (NULL != response_.document.get())) { + char buffer[1024 * 64]; + StreamResult result = Flow(stream.get(), buffer, ARRAY_SIZE(buffer), + response_.document.get()); + if (SR_SUCCESS != result) { + error = HE_STREAM; + } + } + + return error; +} + +bool HttpClient::PrepareValidate() { + ASSERT(CS_READY == cache_state_); + // At this point, request_ contains the pending request, and response_ + // contains the cached response headers. Reformat the request to validate + // the cached content. + HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request_); + HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response_); + if (vs_available < vs_required) { + return false; + } + std::string value; + if (response_.hasHeader(HH_ETAG, &value)) { + request_.addHeader(HH_IF_NONE_MATCH, value); + } + if (response_.hasHeader(HH_LAST_MODIFIED, &value)) { + request_.addHeader(HH_IF_MODIFIED_SINCE, value); + } + response_.clear(false); + cache_state_ = CS_VALIDATING; + return true; +} + +HttpError HttpClient::CompleteValidate() { + ASSERT(CS_VALIDATING == cache_state_); + + std::string id = GetCacheID(server_, request_); + + // Merge cached headers with new headers + HttpError error = ReadCacheHeaders(id, false); + if (HE_NONE != error) { + // Rewrite merged headers to cache + CacheLock lock(cache_, id); + error = WriteCacheHeaders(id); + } + if (HE_NONE != error) { + error = ReadCacheBody(id); + } + return error; +} + +HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked, + size_t data_size) { + if (!ignore_data && !chunked && response_.document.get()) { + // Attempt to pre-allocate space for the downloaded data. + if (!response_.document->ReserveSize(data_size)) { + return HE_OVERFLOW; + } + } + SignalHeaderAvailable(this, chunked, data_size); + return HE_NONE; +} + +// +// HttpBase Implementation +// + +HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) { + if (CS_VALIDATING == cache_state_) { + if (HC_NOT_MODIFIED == response_.scode) { + return CompleteValidate(); + } + // Should we remove conditional headers from request? + cache_state_ = CS_READY; + cache_->DeleteResource(GetCacheID(server_, request_)); + // Continue processing response as normal + } + + ASSERT(!IsCacheActive()); + if ((request_.verb == HV_HEAD) || !HttpCodeHasBody(response_.scode)) { + // HEAD requests and certain response codes contain no body + data_size = 0; + } + if ((HttpCodeIsRedirection(response_.scode) && !fail_redirect_) + || ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode) + && (PROXY_HTTPS == proxy_.type))) { + // We're going to issue another request, so ignore the incoming data. + base_.set_ignore_data(true); + } + + HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size); + if (HE_NONE != error) { + return error; + } + + if ((NULL != cache_) + && !base_.ignore_data() + && HttpShouldCache(request_, response_)) { + if (BeginCacheFile()) { + cache_state_ = CS_WRITING; + } + } + return HE_NONE; +} + +void HttpClient::onHttpComplete(HttpMode mode, HttpError err) { + if (err != HE_NONE) { + // fall through + } else if (mode == HM_CONNECT) { + base_.send(&request_); + return; + } else if ((mode == HM_SEND) || HttpCodeIsInformational(response_.scode)) { + // If you're interested in informational headers, catch + // SignalHeaderAvailable. + base_.recv(&response_); + return; + } else { + if (!HttpShouldKeepAlive(response_)) { + LOG(INFO) << "HttpClient: closing socket"; + base_.stream()->Close(); + } + if (HttpCodeIsRedirection(response_.scode) && !fail_redirect_) { + std::string value; + if (!response_.hasHeader(HH_LOCATION, &value)) { + err = HE_PROTOCOL; + } else { + Url<char> purl(value); + set_server(SocketAddress(purl.server(), purl.port(), false)); + request_.path = purl.full_path(); + if (response_.scode == HC_SEE_OTHER) { + request_.verb = HV_GET; + request_.clearHeader(HH_CONTENT_TYPE); + request_.clearHeader(HH_CONTENT_LENGTH); + request_.document.reset(); + } else if (request_.document.get() && !request_.document->Rewind()) { + // Unable to replay the request document. + err = HE_STREAM; + } + } + if (err == HE_NONE) { + context_.reset(); + response_.clear(false); + release(); + start(); + return; + } + } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response_.scode) + && (PROXY_HTTPS == proxy_.type)) { + std::string response, auth_method; + HttpData::const_iterator begin = response_.begin(HH_PROXY_AUTHENTICATE); + HttpData::const_iterator end = response_.end(HH_PROXY_AUTHENTICATE); + for (HttpData::const_iterator it = begin; it != end; ++it) { + HttpAuthResult res = HttpAuthenticate( + it->second.data(), it->second.size(), + proxy_.address, + ToString(request_.verb), request_.path, + proxy_.username, proxy_.password, + *context_.use(), response, auth_method); + if (res == HAR_RESPONSE) { + request_.setHeader(HH_PROXY_AUTHORIZATION, response); + if (request_.document.get() && !request_.document->Rewind()) { + err = HE_STREAM; + } else { + // Explicitly do not reset the HttpAuthContext + response_.clear(false); + // TODO: Reuse socket when authenticating? + release(); + start(); + return; + } + } else if (res == HAR_IGNORE) { + LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method; + continue; + } else { + break; + } + } + } + } + if (CS_WRITING == cache_state_) { + CompleteCacheFile(); + cache_state_ = CS_READY; + } else if (CS_READING == cache_state_) { + cache_state_ = CS_READY; + } + release(); + SignalHttpClientComplete(this, err); +} + +void HttpClient::onHttpClosed(HttpError err) { + SignalHttpClientClosed(this, err); +} + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/httpclient.h b/Plugins/jingle/libjingle/talk/base/httpclient.h new file mode 100644 index 0000000..789cb59 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpclient.h @@ -0,0 +1,155 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HTTPCLIENT_H__ +#define TALK_BASE_HTTPCLIENT_H__ + +#include "talk/base/common.h" +#include "talk/base/httpbase.h" +#include "talk/base/proxyinfo.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/sigslot.h" +#include "talk/base/socketaddress.h" +#include "talk/base/socketpool.h" + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// HttpClient +////////////////////////////////////////////////////////////////////// + +class DiskCache; +class HttpClient; +class IPNetPool; + +class HttpClient : private IHttpNotify { +public: + HttpClient(const std::string& agent, StreamPool* pool); + virtual ~HttpClient(); + + void set_pool(StreamPool* pool) { pool_ = pool; } + + const std::string& agent() const { return agent_; } + + void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; } + const ProxyInfo& proxy() const { return proxy_; } + + void set_fail_redirect(bool fail_redirect) { fail_redirect_ = fail_redirect; } + bool fail_redirect() const { return fail_redirect_; } + + void use_absolute_uri(bool absolute_uri) { absolute_uri_ = absolute_uri; } + bool absolute_uri() const { return absolute_uri_; } + + void set_cache(DiskCache* cache) { ASSERT(!IsCacheActive()); cache_ = cache; } + bool cache_enabled() const { return (NULL != cache_); } + + // reset clears the server, request, and response structures. It will also + // abort an active request. + void reset(); + + void set_server(const SocketAddress& address); + const SocketAddress& server() const { return server_; } + + HttpRequestData& request() { return request_; } + const HttpRequestData& request() const { return request_; } + HttpResponseData& response() { return response_; } + const HttpResponseData& response() const { return response_; } + + // convenience methods + void prepare_get(const std::string& url); + void prepare_post(const std::string& url, const std::string& content_type, + StreamInterface* request_doc); + + // After you finish setting up your request, call start. + void start(); + + // Signalled when the header has finished downloading, before the document + // content is processed. This notification is for informational purposes + // only. Do not modify the client in response to this. + sigslot::signal3<const HttpClient*,bool,size_t> SignalHeaderAvailable; + // Signalled when the current 'call' finishes. On success, err is 0. + sigslot::signal2<HttpClient*,int> SignalHttpClientComplete; + // Signalled when the network connection goes down while a call is not + // in progress. + sigslot::signal2<HttpClient*,int> SignalHttpClientClosed; + +protected: + void release(); + + bool BeginCacheFile(); + HttpError WriteCacheHeaders(const std::string& id); + void CompleteCacheFile(); + + bool CheckCache(); + HttpError ReadCacheHeaders(const std::string& id, bool override); + HttpError ReadCacheBody(const std::string& id); + + bool PrepareValidate(); + HttpError CompleteValidate(); + + HttpError OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size); + + // IHttpNotify Interface + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size); + virtual void onHttpComplete(HttpMode mode, HttpError err); + virtual void onHttpClosed(HttpError err); + +private: + enum CacheState { CS_READY, CS_WRITING, CS_READING, CS_VALIDATING }; + bool IsCacheActive() const { return (cache_state_ > CS_READY); } + + std::string agent_; + StreamPool* pool_; + HttpBase base_; + SocketAddress server_; + ProxyInfo proxy_; + HttpRequestData request_; + HttpResponseData response_; + bool fail_redirect_, absolute_uri_; + scoped_ptr<HttpAuthContext> context_; + DiskCache* cache_; + CacheState cache_state_; +}; + +////////////////////////////////////////////////////////////////////// +// Default implementation of HttpClient +////////////////////////////////////////////////////////////////////// + +class HttpClientDefault : public ReuseSocketPool, public HttpClient { +public: + HttpClientDefault(SocketFactory* factory, const std::string& agent) + : ReuseSocketPool(factory), HttpClient(agent, NULL) + { + set_pool(this); + } +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_HTTPCLIENT_H__ diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h b/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h new file mode 100644 index 0000000..5235b89 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpcommon-inl.h @@ -0,0 +1,114 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HTTPCOMMON_INL_H__ +#define TALK_BASE_HTTPCOMMON_INL_H__ + +#include "talk/base/common.h" +#include "talk/base/httpcommon.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// Url +/////////////////////////////////////////////////////////////////////////////// + +template<class CTYPE> +Url<CTYPE>::Url(const string& url) { + const CTYPE* raw_url = url.c_str(); + if (ascnicmp(raw_url, "http://", 7) == 0) { + raw_url += 7; + m_secure = false; + } else if (ascnicmp(raw_url, "https://", 8) == 0) { + raw_url += 8; + m_secure = true; + } else { + return; + } + m_port = UrlDefaultPort(m_secure); + const CTYPE* colon = ::strchr(raw_url, static_cast<CTYPE>(':')); + const CTYPE* slash = ::strchr(raw_url, static_cast<CTYPE>('/')); + if (!colon && !slash) { + m_server = url; + // TODO: rethink this slash + m_path.append(1, static_cast<CTYPE>('/')); + } else { + const CTYPE* ptr; + if (colon == 0) { + ptr = slash; + } else if (slash == 0) { + ptr = colon; + } else { + ptr = _min(colon, slash); + } + m_server.assign(raw_url, ptr - raw_url); + if (ptr == colon) { + CTYPE* tmp = 0; + m_port = static_cast<uint16>(::strtoul(ptr + 1, &tmp, 10)); + ptr = tmp; + } + const CTYPE* query = ::strchr(ptr, static_cast<CTYPE>('?')); + if (!query) { + m_path.assign(ptr); + } else { + m_path.assign(ptr, query - ptr); + m_query.assign(query); + } + } + ASSERT(m_path.empty() || (m_path[0] == static_cast<CTYPE>('/'))); + ASSERT(m_query.empty() || (m_query[0] == static_cast<CTYPE>('?'))); +} + +template<class CTYPE> +typename Traits<CTYPE>::string Url<CTYPE>::full_path() { + string full_path(m_path); + full_path.append(m_query); + return full_path; +} + +template<class CTYPE> +typename Traits<CTYPE>::string Url<CTYPE>::url() { + CTYPE protocol[9]; + asccpyn(protocol, ARRAY_SIZE(protocol), m_secure ? "https://" : "http://"); + string url(protocol); + url.append(m_server); + if (m_port != UrlDefaultPort(m_secure)) { + CTYPE format[5], port[32]; + asccpyn(format, ARRAY_SIZE(format), ":%hu"); + sprintfn(port, ARRAY_SIZE(port), format, m_port); + url.append(port); + } + url.append(m_path); + url.append(m_query); + return url; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_HTTPCOMMON_INL_H__ diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon.cc b/Plugins/jingle/libjingle/talk/base/httpcommon.cc new file mode 100644 index 0000000..0358121 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpcommon.cc @@ -0,0 +1,940 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <time.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#define _WINSOCKAPI_ +#include <windows.h> +#define SECURITY_WIN32 +#include <security.h> +#endif + +#include "talk/base/base64.h" +#include "talk/base/common.h" +#include "talk/base/cryptstring.h" +#include "talk/base/httpcommon.h" +#include "talk/base/socketaddress.h" +#include "talk/base/stringdigest.h" +#include "talk/base/stringutils.h" + +namespace talk_base { + +#ifdef WIN32 +extern const ConstantLabel SECURITY_ERRORS[]; +#endif + +////////////////////////////////////////////////////////////////////// +// Enum - TODO: expose globally later? +////////////////////////////////////////////////////////////////////// + +bool find_string(size_t& index, const std::string& needle, + const char* const haystack[], size_t max_index) { + for (index=0; index<max_index; ++index) { + if (_stricmp(needle.c_str(), haystack[index]) == 0) { + return true; + } + } + return false; +} + +template<class E> +struct Enum { + static const char** Names; + static size_t Size; + + static inline const char* Name(E val) { return Names[val]; } + static inline bool Parse(E& val, const std::string& name) { + size_t index; + if (!find_string(index, name, Names, Size)) + return false; + val = static_cast<E>(index); + return true; + } + + E val; + + inline operator E&() { return val; } + inline Enum& operator=(E rhs) { val = rhs; return *this; } + + inline const char* name() const { return Name(val); } + inline bool assign(const std::string& name) { return Parse(val, name); } + inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; } +}; + +#define ENUM(e,n) \ + template<> const char** Enum<e>::Names = n; \ + template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0]) + +////////////////////////////////////////////////////////////////////// +// HttpCommon +////////////////////////////////////////////////////////////////////// + +static const char* kHttpVersions[HVER_LAST+1] = { + "1.0", "1.1" +}; +ENUM(HttpVersion, kHttpVersions); + +static const char* kHttpVerbs[HV_LAST+1] = { + "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD" +}; +ENUM(HttpVerb, kHttpVerbs); + +static const char* kHttpHeaders[HH_LAST+1] = { + "Age", + "Cache-Control", + "Connection", + "Content-Length", + "Content-Range", + "Content-Type", + "Cookie", + "Date", + "ETag", + "Expires", + "Host", + "If-Modified-Since", + "If-None-Match", + "Keep-Alive", + "Last-Modified", + "Location", + "Proxy-Authenticate", + "Proxy-Authorization", + "Proxy-Connection", + "Range", + "Set-Cookie", + "TE", + "Trailers", + "Transfer-Encoding", + "Upgrade", + "User-Agent", + "WWW-Authenticate", +}; +ENUM(HttpHeader, kHttpHeaders); + +const char* ToString(HttpVersion version) { + return Enum<HttpVersion>::Name(version); +} + +bool FromString(HttpVersion& version, const std::string& str) { + return Enum<HttpVersion>::Parse(version, str); +} + +const char* ToString(HttpVerb verb) { + return Enum<HttpVerb>::Name(verb); +} + +bool FromString(HttpVerb& verb, const std::string& str) { + return Enum<HttpVerb>::Parse(verb, str); +} + +const char* ToString(HttpHeader header) { + return Enum<HttpHeader>::Name(header); +} + +bool FromString(HttpHeader& header, const std::string& str) { + return Enum<HttpHeader>::Parse(header, str); +} + +bool HttpCodeHasBody(uint32 code) { + return !HttpCodeIsInformational(code) + && (code != HC_NO_CONTENT) || (code != HC_NOT_MODIFIED); +} + +bool HttpCodeIsCacheable(uint32 code) { + switch (code) { + case HC_OK: + case HC_NON_AUTHORITATIVE: + case HC_PARTIAL_CONTENT: + case HC_MULTIPLE_CHOICES: + case HC_MOVED_PERMANENTLY: + case HC_GONE: + return true; + default: + return false; + } +} + +bool HttpHeaderIsEndToEnd(HttpHeader header) { + switch (header) { + case HH_CONNECTION: + case HH_KEEP_ALIVE: + case HH_PROXY_AUTHENTICATE: + case HH_PROXY_AUTHORIZATION: + //case HH_PROXY_CONNECTION:?? + case HH_TE: + case HH_TRAILERS: + case HH_TRANSFER_ENCODING: + case HH_UPGRADE: + return false; + default: + return true; + } +} + +bool HttpHeaderIsCollapsible(HttpHeader header) { + switch (header) { + case HH_SET_COOKIE: + case HH_PROXY_AUTHENTICATE: + case HH_WWW_AUTHENTICATE: + return false; + default: + return true; + } +} + +bool HttpShouldKeepAlive(const HttpData& data) { + std::string connection; + if ((data.hasHeader(HH_PROXY_CONNECTION, &connection) + || data.hasHeader(HH_CONNECTION, &connection))) { + return (_stricmp(connection.c_str(), "Keep-Alive") == 0); + } + return (data.version >= HVER_1_1); +} + +namespace { + +inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) { + if (pos >= len) + return true; + if (isspace(static_cast<unsigned char>(data[pos]))) + return true; + // The reason for this complexity is that some attributes may contain trailing + // equal signs (like base64 tokens in Negotiate auth headers) + if ((pos+1 < len) && (data[pos] == '=') && + !isspace(static_cast<unsigned char>(data[pos+1])) && + (data[pos+1] != '=')) { + return true; + } + return false; +} + +} // anonymous namespace + +void HttpParseAttributes(const char * data, size_t len, + HttpAttributeList& attributes) { + size_t pos = 0; + while (true) { + // Skip leading whitespace + while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) { + ++pos; + } + + // End of attributes? + if (pos >= len) + return; + + // Find end of attribute name + size_t start = pos; + while (!IsEndOfAttributeName(pos, len, data)) { + ++pos; + } + + HttpAttribute attribute; + attribute.first.assign(data + start, data + pos); + + // Attribute has value? + if ((pos < len) && (data[pos] == '=')) { + ++pos; // Skip '=' + // Check if quoted value + if ((pos < len) && (data[pos] == '"')) { + while (++pos < len) { + if (data[pos] == '"') { + ++pos; + break; + } + if ((data[pos] == '\\') && (pos + 1 < len)) + ++pos; + attribute.second.append(1, data[pos]); + } + } else { + while ((pos < len) && + !isspace(static_cast<unsigned char>(data[pos])) && + (data[pos] != ',')) { + attribute.second.append(1, data[pos++]); + } + } + } + + attributes.push_back(attribute); + if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' + } +} + +bool HttpHasAttribute(const HttpAttributeList& attributes, + const std::string& name, + std::string* value) { + for (HttpAttributeList::const_iterator it = attributes.begin(); + it != attributes.end(); ++it) { + if (it->first == name) { + if (value) { + *value = it->second; + } + return true; + } + } + return false; +} + +bool HttpHasNthAttribute(HttpAttributeList& attributes, + size_t index, + std::string* name, + std::string* value) { + if (index >= attributes.size()) + return false; + + if (name) + *name = attributes[index].first; + if (value) + *value = attributes[index].second; + return true; +} + +bool HttpDateToSeconds(const std::string& date, unsigned long* seconds) { + const char* const kTimeZones[] = { + "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y" + }; + const int kTimeZoneOffsets[] = { + 0, 0, -5, -4, -6, -5, -7, -6, -8, -7, + -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 + }; + + ASSERT(NULL != seconds); + struct tm tval; + memset(&tval, 0, sizeof(tval)); + char month[4], zone[6]; + memset(month, 0, sizeof(month)); + memset(zone, 0, sizeof(zone)); + + if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c", + &tval.tm_mday, month, &tval.tm_year, + &tval.tm_hour, &tval.tm_min, &tval.tm_sec, &zone)) { + return false; + } + switch (toupper(month[2])) { + case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break; + case 'B': tval.tm_mon = 1; break; + case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break; + case 'Y': tval.tm_mon = 4; break; + case 'L': tval.tm_mon = 6; break; + case 'G': tval.tm_mon = 7; break; + case 'P': tval.tm_mon = 8; break; + case 'T': tval.tm_mon = 9; break; + case 'V': tval.tm_mon = 10; break; + case 'C': tval.tm_mon = 11; break; + } + tval.tm_year -= 1900; + unsigned long gmt, non_gmt = mktime(&tval); + if ((zone[0] == '+') || (zone[0] == '-')) { + if (!isdigit(zone[1]) || !isdigit(zone[2]) + || !isdigit(zone[3]) || !isdigit(zone[4])) { + return false; + } + int hours = (zone[1] - '0') * 10 + (zone[2] - '0'); + int minutes = (zone[3] - '0') * 10 + (zone[4] - '0'); + int offset = (hours * 60 + minutes) * 60; + gmt = non_gmt + (zone[0] == '+') ? offset : -offset; + } else { + size_t zindex; + if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) { + return false; + } + gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60; + } +#ifdef OSX + tm *tm_for_timezone = localtime((time_t *)&gmt); + *seconds = gmt + tm_for_timezone->tm_gmtoff; +#else + *seconds = gmt - timezone; +#endif + return true; +} + +////////////////////////////////////////////////////////////////////// +// HttpData +////////////////////////////////////////////////////////////////////// + +void +HttpData::clear(bool release_document) { + if (release_document) { + document.reset(); + } + m_headers.clear(); +} + +void +HttpData::changeHeader(const std::string& name, const std::string& value, + HeaderCombine combine) { + if (combine == HC_AUTO) { + HttpHeader header; + // Unrecognized headers are collapsible + combine = !FromString(header, name) || HttpHeaderIsCollapsible(header) + ? HC_YES : HC_NO; + } else if (combine == HC_REPLACE) { + m_headers.erase(name); + combine = HC_NO; + } + // At this point, combine is one of (YES, NO, NEW) + if (combine != HC_NO) { + HeaderMap::iterator it = m_headers.find(name); + if (it != m_headers.end()) { + if (combine == HC_YES) { + it->second.append(","); + it->second.append(value); + } + return; + } + } + m_headers.insert(HeaderMap::value_type(name, value)); +} + +void +HttpData::clearHeader(const std::string& name) { + m_headers.erase(name); +} + +bool +HttpData::hasHeader(const std::string& name, std::string* value) const { + HeaderMap::const_iterator it = m_headers.find(name); + if (it == m_headers.end()) { + return false; + } else if (value) { + *value = it->second; + } + return true; +} + +void +HttpData::setContent(const std::string& content_type, + StreamInterface* document) { + ASSERT(document != NULL); + this->document.reset(document); + setHeader(HH_CONTENT_TYPE, content_type); + size_t content_length = 0; + if (this->document->GetSize(&content_length)) { + char buffer[32]; + sprintfn(buffer, sizeof(buffer), "%d", content_length); + setHeader(HH_CONTENT_LENGTH, buffer); + } else { + setHeader(HH_TRANSFER_ENCODING, "chunked"); + } +} + +// +// HttpRequestData +// + +void +HttpRequestData::clear(bool release_document) { + HttpData::clear(release_document); + verb = HV_GET; + path.clear(); +} + +size_t +HttpRequestData::formatLeader(char* buffer, size_t size) { + ASSERT(path.find(' ') == std::string::npos); + return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(), + path.data(), ToString(version)); +} + +HttpError +HttpRequestData::parseLeader(const char* line, size_t len) { + UNUSED(len); + uint32 vmajor, vminor; + int vend, dstart, dend; + if ((sscanf(line, "%*s%n %n%*s%n HTTP/%lu.%lu", &vend, &dstart, &dend, + &vmajor, &vminor) != 2) + || (vmajor != 1)) { + return HE_PROTOCOL; + } + if (vminor == 0) { + version = HVER_1_0; + } else if (vminor == 1) { + version = HVER_1_1; + } else { + return HE_PROTOCOL; + } + std::string sverb(line, vend); + if (!FromString(verb, sverb.c_str())) { + return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED? + } + path.assign(line + dstart, line + dend); + return HE_NONE; +} + +// +// HttpResponseData +// + +void +HttpResponseData::clear(bool release_document) { + HttpData::clear(release_document); + scode = HC_INTERNAL_SERVER_ERROR; + message.clear(); +} + +void +HttpResponseData::set_success(uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_CONTENT_LENGTH, "0"); +} + +void +HttpResponseData::set_success(const std::string& content_type, + StreamInterface* document, + uint32 scode) { + this->scode = scode; + message.erase(message.begin(), message.end()); + setContent(content_type, document); +} + +void +HttpResponseData::set_redirect(const std::string& location, uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_LOCATION, location); + setHeader(HH_CONTENT_LENGTH, "0"); +} + +void +HttpResponseData::set_error(uint32 scode) { + this->scode = scode; + message.clear(); + setHeader(HH_CONTENT_LENGTH, "0"); +} + +size_t +HttpResponseData::formatLeader(char* buffer, size_t size) { + size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode); + if (!message.empty()) { + len += sprintfn(buffer + len, size - len, " %.*s", + message.size(), message.data()); + } + return len; +} + +HttpError +HttpResponseData::parseLeader(const char* line, size_t len) { + size_t pos = 0; + uint32 vmajor, vminor; + if ((sscanf(line, "HTTP/%lu.%lu %lu%n", &vmajor, &vminor, &scode, &pos) != 3) + || (vmajor != 1)) { + return HE_PROTOCOL; + } + if (vminor == 0) { + version = HVER_1_0; + } else if (vminor == 1) { + version = HVER_1_1; + } else { + return HE_PROTOCOL; + } + while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos; + message.assign(line + pos, len - pos); + return HE_NONE; +} + +////////////////////////////////////////////////////////////////////// +// Http Authentication +////////////////////////////////////////////////////////////////////// + +#define TEST_DIGEST 0 +#if TEST_DIGEST +/* +const char * const DIGEST_CHALLENGE = + "Digest realm=\"testrealm@host.com\"," + " qop=\"auth,auth-int\"," + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; +const char * const DIGEST_METHOD = "GET"; +const char * const DIGEST_URI = + "/dir/index.html";; +const char * const DIGEST_CNONCE = + "0a4f113b"; +const char * const DIGEST_RESPONSE = + "6629fae49393a05397450978507c4ef1"; +//user_ = "Mufasa"; +//pass_ = "Circle Of Life"; +*/ +const char * const DIGEST_CHALLENGE = + "Digest realm=\"Squid proxy-caching web server\"," + " nonce=\"Nny4QuC5PwiSDixJ\"," + " qop=\"auth\"," + " stale=false"; +const char * const DIGEST_URI = + "/"; +const char * const DIGEST_CNONCE = + "6501d58e9a21cee1e7b5fec894ded024"; +const char * const DIGEST_RESPONSE = + "edffcb0829e755838b073a4a42de06bc"; +#endif + +static std::string quote(const std::string& str) { + std::string result; + result.push_back('"'); + for (size_t i=0; i<str.size(); ++i) { + if ((str[i] == '"') || (str[i] == '\\')) + result.push_back('\\'); + result.push_back(str[i]); + } + result.push_back('"'); + return result; +} + +#ifdef WIN32 +struct NegotiateAuthContext : public HttpAuthContext { + CredHandle cred; + CtxtHandle ctx; + size_t steps; + bool specified_credentials; + + NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2) + : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0), + specified_credentials(false) + { } + + virtual ~NegotiateAuthContext() { + DeleteSecurityContext(&ctx); + FreeCredentialsHandle(&cred); + } +}; +#endif // WIN32 + +HttpAuthResult HttpAuthenticate( + const char * challenge, size_t len, + const SocketAddress& server, + const std::string& method, const std::string& uri, + const std::string& username, const CryptString& password, + HttpAuthContext *& context, std::string& response, std::string& auth_method) +{ +#if TEST_DIGEST + challenge = DIGEST_CHALLENGE; + len = strlen(challenge); +#endif + + HttpAttributeList args; + HttpParseAttributes(challenge, len, args); + HttpHasNthAttribute(args, 0, &auth_method, NULL); + + if (context && (context->auth_method != auth_method)) + return HAR_IGNORE; + + // BASIC + if (_stricmp(auth_method.c_str(), "basic") == 0) { + if (context) + return HAR_CREDENTIALS; // Bad credentials + if (username.empty()) + return HAR_CREDENTIALS; // Missing credentials + + context = new HttpAuthContext(auth_method); + + // TODO: convert sensitive to a secure buffer that gets securely deleted + //std::string decoded = username + ":" + password; + size_t len = username.size() + password.GetLength() + 2; + char * sensitive = new char[len]; + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + response = auth_method; + response.append(" "); + // TODO: create a sensitive-source version of Base64::encode + response.append(Base64::encode(sensitive)); + memset(sensitive, 0, len); + delete [] sensitive; + return HAR_RESPONSE; + } + + // DIGEST + if (_stricmp(auth_method.c_str(), "digest") == 0) { + if (context) + return HAR_CREDENTIALS; // Bad credentials + if (username.empty()) + return HAR_CREDENTIALS; // Missing credentials + + context = new HttpAuthContext(auth_method); + + std::string cnonce, ncount; +#if TEST_DIGEST + method = DIGEST_METHOD; + uri = DIGEST_URI; + cnonce = DIGEST_CNONCE; +#else + char buffer[256]; + sprintf(buffer, "%d", time(0)); + cnonce = MD5(buffer); +#endif + ncount = "00000001"; + + std::string realm, nonce, qop, opaque; + HttpHasAttribute(args, "realm", &realm); + HttpHasAttribute(args, "nonce", &nonce); + bool has_qop = HttpHasAttribute(args, "qop", &qop); + bool has_opaque = HttpHasAttribute(args, "opaque", &opaque); + + // TODO: convert sensitive to be secure buffer + //std::string A1 = username + ":" + realm + ":" + password; + size_t len = username.size() + realm.size() + password.GetLength() + 3; + char * sensitive = new char[len]; // A1 + size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + pos += strcpyn(sensitive + pos, len - pos, realm.c_str()); + pos += strcpyn(sensitive + pos, len - pos, ":"); + password.CopyTo(sensitive + pos, true); + + std::string A2 = method + ":" + uri; + std::string middle; + if (has_qop) { + qop = "auth"; + middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop; + } else { + middle = nonce; + } + std::string HA1 = MD5(sensitive); + memset(sensitive, 0, len); + delete [] sensitive; + std::string HA2 = MD5(A2); + std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2); + +#if TEST_DIGEST + assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0); +#endif + + std::stringstream ss; + ss << auth_method; + ss << " username=" << quote(username); + ss << ", realm=" << quote(realm); + ss << ", nonce=" << quote(nonce); + ss << ", uri=" << quote(uri); + if (has_qop) { + ss << ", qop=" << qop; + ss << ", nc=" << ncount; + ss << ", cnonce=" << quote(cnonce); + } + ss << ", response=\"" << dig_response << "\""; + if (has_opaque) { + ss << ", opaque=" << quote(opaque); + } + response = ss.str(); + return HAR_RESPONSE; + } + +#ifdef WIN32 +#if 1 + bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0); + bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0); + // SPNEGO & NTLM + if (want_negotiate || want_ntlm) { + const size_t MAX_MESSAGE = 12000, MAX_SPN = 256; + char out_buf[MAX_MESSAGE], spn[MAX_SPN]; + +#if 0 // Requires funky windows versions + DWORD len = MAX_SPN; + if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(), + 0, &len, spn) != ERROR_SUCCESS) { + LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed"; + return HAR_IGNORE; + } +#else + sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str()); +#endif + + SecBuffer out_sec; + out_sec.pvBuffer = out_buf; + out_sec.cbBuffer = sizeof(out_buf); + out_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc out_buf_desc; + out_buf_desc.ulVersion = 0; + out_buf_desc.cBuffers = 1; + out_buf_desc.pBuffers = &out_sec; + + const ULONG NEG_FLAGS_DEFAULT = + //ISC_REQ_ALLOCATE_MEMORY + ISC_REQ_CONFIDENTIALITY + //| ISC_REQ_EXTENDED_ERROR + //| ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + //| ISC_REQ_STREAM + //| ISC_REQ_USE_SUPPLIED_CREDS + ; + + TimeStamp lifetime; + SECURITY_STATUS ret = S_OK; + ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT; + + bool specify_credentials = !username.empty(); + size_t steps = 0; + + //uint32 now = Time(); + + NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context); + if (neg) { + const size_t max_steps = 10; + if (++neg->steps >= max_steps) { + LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries"; + return HAR_ERROR; + } + steps = neg->steps; + + std::string decoded_challenge; + HttpHasNthAttribute(args, 1, &decoded_challenge, NULL); + decoded_challenge = Base64::decode(decoded_challenge); + if (!decoded_challenge.empty()) { + SecBuffer in_sec; + in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data()); + in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size()); + in_sec.BufferType = SECBUFFER_TOKEN; + + SecBufferDesc in_buf_desc; + in_buf_desc.ulVersion = 0; + in_buf_desc.cBuffers = 1; + in_buf_desc.pBuffers = &in_sec; + + ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeDiff(talk_base::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + return HAR_ERROR; + } + } else if (neg->specified_credentials) { + // Try again with default credentials + specify_credentials = false; + delete context; + context = neg = 0; + } else { + return HAR_CREDENTIALS; + } + } + + if (!neg) { + unsigned char userbuf[256], passbuf[256], domainbuf[16]; + SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0; + if (specify_credentials) { + memset(&auth_id, 0, sizeof(auth_id)); + size_t len = password.GetLength()+1; + char * sensitive = new char[len]; + password.CopyTo(sensitive, true); + std::string::size_type pos = username.find('\\'); + if (pos == std::string::npos) { + auth_id.UserLength = static_cast<unsigned long>( + _min(sizeof(userbuf) - 1, username.size())); + memcpy(userbuf, username.c_str(), auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = 0; + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast<unsigned long>( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } else { + auth_id.UserLength = static_cast<unsigned long>( + _min(sizeof(userbuf) - 1, username.size() - pos - 1)); + memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + userbuf[auth_id.UserLength] = 0; + auth_id.DomainLength = static_cast<unsigned long>( + _min(sizeof(domainbuf) - 1, pos)); + memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + domainbuf[auth_id.DomainLength] = 0; + auth_id.PasswordLength = static_cast<unsigned long>( + _min(sizeof(passbuf) - 1, password.GetLength())); + memcpy(passbuf, sensitive, auth_id.PasswordLength); + passbuf[auth_id.PasswordLength] = 0; + } + memset(sensitive, 0, len); + delete [] sensitive; + auth_id.User = userbuf; + auth_id.Domain = domainbuf; + auth_id.Password = passbuf; + auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + pauth_id = &auth_id; + LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials"; + } else { + LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials"; + } + + CredHandle cred; + ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); + //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeDiff(talk_base::Time(), now); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return HAR_IGNORE; + } + + //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out; + + CtxtHandle ctx; + ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); + //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeDiff(talk_base::Time(), now); + if (FAILED(ret)) { + LOG(LS_ERROR) << "InitializeSecurityContext returned: " + << ErrorName(ret, SECURITY_ERRORS); + FreeCredentialsHandle(&cred); + return HAR_IGNORE; + } + + assert(!context); + context = neg = new NegotiateAuthContext(auth_method, cred, ctx); + neg->specified_credentials = specify_credentials; + neg->steps = steps; + } + + if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { + ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); + //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeDiff(talk_base::Time(), now); + LOG(LS_VERBOSE) << "CompleteAuthToken returned: " + << ErrorName(ret, SECURITY_ERRORS); + if (FAILED(ret)) { + return HAR_ERROR; + } + } + + //LOG(INFO) << "$$$ NEGOTIATE took " << TimeDiff(talk_base::Time(), now) << "ms"; + + std::string decoded(out_buf, out_buf + out_sec.cbBuffer); + response = auth_method; + response.append(" "); + response.append(Base64::encode(decoded)); + return HAR_RESPONSE; + } +#endif +#endif // WIN32 + + return HAR_IGNORE; +} + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/httpcommon.h b/Plugins/jingle/libjingle/talk/base/httpcommon.h new file mode 100644 index 0000000..2893933 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpcommon.h @@ -0,0 +1,373 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HTTPCOMMON_H__ +#define TALK_BASE_HTTPCOMMON_H__ + +#include <map> +#include <string> +#include <vector> +#include "talk/base/basictypes.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/stringutils.h" +#include "talk/base/stream.h" + +namespace talk_base { + +class CryptString; +class SocketAddress; + +////////////////////////////////////////////////////////////////////// +// Constants +////////////////////////////////////////////////////////////////////// + +enum HttpCode { + HC_OK = 200, + HC_NON_AUTHORITATIVE = 203, + HC_NO_CONTENT = 204, + HC_PARTIAL_CONTENT = 206, + + HC_MULTIPLE_CHOICES = 300, + HC_MOVED_PERMANENTLY = 301, + HC_FOUND = 302, + HC_SEE_OTHER = 303, + HC_NOT_MODIFIED = 304, + HC_MOVED_TEMPORARILY = 307, + + HC_BAD_REQUEST = 400, + HC_UNAUTHORIZED = 401, + HC_FORBIDDEN = 403, + HC_NOT_FOUND = 404, + HC_PROXY_AUTHENTICATION_REQUIRED = 407, + HC_GONE = 410, + + HC_INTERNAL_SERVER_ERROR = 500 +}; + +enum HttpVersion { + HVER_1_0, HVER_1_1, + HVER_LAST = HVER_1_1 +}; + +enum HttpVerb { + HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD, + HV_LAST = HV_HEAD +}; + +enum HttpError { + HE_NONE, + HE_PROTOCOL, HE_DISCONNECTED, HE_OVERFLOW, + HE_SOCKET, HE_SHUTDOWN, HE_OPERATION_CANCELLED, + HE_AUTH, // Proxy Authentication Required + HE_CERTIFICATE_EXPIRED, // During SSL negotiation + HE_STREAM, // Problem reading or writing to the document + HE_CACHE, // Problem reading from cache + HE_DEFAULT +}; + +enum HttpHeader { + HH_AGE, + HH_CACHE_CONTROL, + HH_CONNECTION, + HH_CONTENT_LENGTH, + HH_CONTENT_RANGE, + HH_CONTENT_TYPE, + HH_COOKIE, + HH_DATE, + HH_ETAG, + HH_EXPIRES, + HH_HOST, + HH_IF_MODIFIED_SINCE, + HH_IF_NONE_MATCH, + HH_KEEP_ALIVE, + HH_LAST_MODIFIED, + HH_LOCATION, + HH_PROXY_AUTHENTICATE, + HH_PROXY_AUTHORIZATION, + HH_PROXY_CONNECTION, + HH_RANGE, + HH_SET_COOKIE, + HH_TE, + HH_TRAILERS, + HH_TRANSFER_ENCODING, + HH_UPGRADE, + HH_USER_AGENT, + HH_WWW_AUTHENTICATE, + HH_LAST = HH_WWW_AUTHENTICATE +}; + +const uint16 HTTP_DEFAULT_PORT = 80; +const uint16 HTTP_SECURE_PORT = 443; + +////////////////////////////////////////////////////////////////////// +// Utility Functions +////////////////////////////////////////////////////////////////////// + +inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) { + return (err != HE_NONE) ? err : def_err; +} + +const char* ToString(HttpVersion version); +bool FromString(HttpVersion& version, const std::string& str); + +const char* ToString(HttpVerb verb); +bool FromString(HttpVerb& verb, const std::string& str); + +const char* ToString(HttpHeader header); +bool FromString(HttpHeader& header, const std::string& str); + +inline bool HttpCodeIsInformational(uint32 code) { return ((code / 100) == 1); } +inline bool HttpCodeIsSuccessful(uint32 code) { return ((code / 100) == 2); } +inline bool HttpCodeIsRedirection(uint32 code) { return ((code / 100) == 3); } +inline bool HttpCodeIsClientError(uint32 code) { return ((code / 100) == 4); } +inline bool HttpCodeIsServerError(uint32 code) { return ((code / 100) == 5); } + +bool HttpCodeHasBody(uint32 code); +bool HttpCodeIsCacheable(uint32 code); +bool HttpHeaderIsEndToEnd(HttpHeader header); +bool HttpHeaderIsCollapsible(HttpHeader header); + +struct HttpData; +bool HttpShouldKeepAlive(const HttpData& data); + +typedef std::pair<std::string, std::string> HttpAttribute; +typedef std::vector<HttpAttribute> HttpAttributeList; +void HttpParseAttributes(const char * data, size_t len, + HttpAttributeList& attributes); +bool HttpHasAttribute(const HttpAttributeList& attributes, + const std::string& name, + std::string* value); +bool HttpHasNthAttribute(HttpAttributeList& attributes, + size_t index, + std::string* name, + std::string* value); + +// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp +bool HttpDateToSeconds(const std::string& date, unsigned long* seconds); + +inline const uint16 UrlDefaultPort(bool secure) { + return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT; +} + +// functional for insensitive std::string compare +struct iless { + bool operator()(const std::string& lhs, const std::string& rhs) const { + return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0); + } +}; + +////////////////////////////////////////////////////////////////////// +// Url +////////////////////////////////////////////////////////////////////// + +template<class CTYPE> +class Url { +public: + typedef typename Traits<CTYPE>::string string; + + // TODO: Implement Encode/Decode + static int Encode(const CTYPE* source, CTYPE* destination, size_t len); + static int Encode(const string& source, string& destination); + static int Decode(const CTYPE* source, CTYPE* destination, size_t len); + static int Decode(const string& source, string& destination); + + Url(const string& url); + Url(const string& path, const string& server, uint16 port = HTTP_DEFAULT_PORT) + : m_server(server), m_path(path), m_port(port), + m_secure(HTTP_SECURE_PORT == port) + { + ASSERT(m_path.empty() || (m_path[0] == static_cast<CTYPE>('/'))); + } + + bool valid() const { return !m_server.empty(); } + const string& server() const { return m_server; } + // Note: path() was renamed to path_, because it now uses the stricter sense + // of not including a query string. I'm trying to think of a clearer name. + const string& path_() const { return m_path; } + const string& query() const { return m_query; } + string full_path(); + string url(); + uint16 port() const { return m_port; } + bool secure() const { return m_secure; } + + void set_server(const string& val) { m_server = val; } + void set_path(const string& val) { + ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('/'))); + m_path = val; + } + void set_query(const string& val) { + ASSERT(val.empty() || (val[0] == static_cast<CTYPE>('?'))); + m_query = val; + } + void set_port(uint16 val) { m_port = val; } + void set_secure(bool val) { m_secure = val; } + +private: + string m_server, m_path, m_query; + uint16 m_port; + bool m_secure; +}; + +////////////////////////////////////////////////////////////////////// +// HttpData +////////////////////////////////////////////////////////////////////// + +struct HttpData { + typedef std::multimap<std::string, std::string, iless> HeaderMap; + typedef HeaderMap::const_iterator const_iterator; + + HttpVersion version; + scoped_ptr<StreamInterface> document; + + HttpData() : version(HVER_1_1) { } + + enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW }; + void changeHeader(const std::string& name, const std::string& value, + HeaderCombine combine); + inline void addHeader(const std::string& name, const std::string& value, + bool append = true) { + changeHeader(name, value, append ? HC_AUTO : HC_NO); + } + inline void setHeader(const std::string& name, const std::string& value, + bool overwrite = true) { + changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW); + } + void clearHeader(const std::string& name); + + // keep in mind, this may not do what you want in the face of multiple headers + bool hasHeader(const std::string& name, std::string* value) const; + + inline const_iterator begin() const { + return m_headers.begin(); + } + inline const_iterator end() const { + return m_headers.end(); + } + inline const_iterator begin(const std::string& name) const { + return m_headers.lower_bound(name); + } + inline const_iterator end(const std::string& name) const { + return m_headers.upper_bound(name); + } + + // Convenience methods using HttpHeader + inline void changeHeader(HttpHeader header, const std::string& value, + HeaderCombine combine) { + changeHeader(ToString(header), value, combine); + } + inline void addHeader(HttpHeader header, const std::string& value, + bool append = true) { + addHeader(ToString(header), value, append); + } + inline void setHeader(HttpHeader header, const std::string& value, + bool overwrite = true) { + setHeader(ToString(header), value, overwrite); + } + inline void clearHeader(HttpHeader header) { + clearHeader(ToString(header)); + } + inline bool hasHeader(HttpHeader header, std::string* value) const { + return hasHeader(ToString(header), value); + } + inline const_iterator begin(HttpHeader header) const { + return m_headers.lower_bound(ToString(header)); + } + inline const_iterator end(HttpHeader header) const { + return m_headers.upper_bound(ToString(header)); + } + + void setContent(const std::string& content_type, StreamInterface* document); + + virtual size_t formatLeader(char* buffer, size_t size) = 0; + virtual HttpError parseLeader(const char* line, size_t len) = 0; + +protected: + virtual ~HttpData() { } + void clear(bool release_document); + +private: + HeaderMap m_headers; +}; + +struct HttpRequestData : public HttpData { + HttpVerb verb; + std::string path; + + HttpRequestData() : verb(HV_GET) { } + + void clear(bool release_document); + + virtual size_t formatLeader(char* buffer, size_t size); + virtual HttpError parseLeader(const char* line, size_t len); +}; + +struct HttpResponseData : public HttpData { + uint32 scode; + std::string message; + + HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { } + void clear(bool release_document); + + // Convenience methods + void set_success(uint32 scode = HC_OK); + void set_success(const std::string& content_type, StreamInterface* document, + uint32 scode = HC_OK); + void set_redirect(const std::string& location, + uint32 scode = HC_MOVED_TEMPORARILY); + void set_error(uint32 scode); + + virtual size_t formatLeader(char* buffer, size_t size); + virtual HttpError parseLeader(const char* line, size_t len); +}; + +////////////////////////////////////////////////////////////////////// +// Http Authentication +////////////////////////////////////////////////////////////////////// + +struct HttpAuthContext { + std::string auth_method; + HttpAuthContext(const std::string& auth) : auth_method(auth) { } + virtual ~HttpAuthContext() { } +}; + +enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR }; + +// 'context' is used by this function to record information between calls. +// Start by passing a null pointer, then pass the same pointer each additional +// call. When the authentication attempt is finished, delete the context. +HttpAuthResult HttpAuthenticate( + const char * challenge, size_t len, + const SocketAddress& server, + const std::string& method, const std::string& uri, + const std::string& username, const CryptString& password, + HttpAuthContext *& context, std::string& response, std::string& auth_method); + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_HTTPCOMMON_H__ diff --git a/Plugins/jingle/libjingle/talk/base/httpserver.cc b/Plugins/jingle/libjingle/talk/base/httpserver.cc new file mode 100644 index 0000000..9c27b5c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpserver.cc @@ -0,0 +1,261 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> + +#include "talk/base/httpcommon-inl.h" + +#include "talk/base/asyncsocket.h" +#include "talk/base/common.h" +#include "talk/base/httpserver.h" +#include "talk/base/logging.h" +#include "talk/base/socketstream.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// HttpServer +/////////////////////////////////////////////////////////////////////////////// + +HttpServer::HttpServer() : next_connection_id_(1) { +} + +HttpServer::~HttpServer() { + for (ConnectionMap::iterator it = connections_.begin(); + it != connections_.end(); + ++it) { + StreamInterface* stream = it->second->EndProcess(); + delete stream; + delete it->second; + } +} + +int +HttpServer::HandleConnection(StreamInterface* stream) { + int connection_id = next_connection_id_++; + ASSERT(connection_id != HTTP_INVALID_CONNECTION_ID); + Connection* connection = new Connection(connection_id, this); + connections_.insert(ConnectionMap::value_type(connection_id, connection)); + connection->BeginProcess(stream); + return connection_id; +} + +void +HttpServer::Respond(HttpTransaction* transaction) { + int connection_id = transaction->connection_id(); + if (Connection* connection = Find(connection_id)) { + connection->Respond(transaction); + } else { + delete transaction; + // We may be tempted to SignalHttpComplete, but that implies that a + // connection still exists. + } +} + +void +HttpServer::Close(int connection_id, bool force) { + if (Connection* connection = Find(connection_id)) { + connection->InitiateClose(force); + } +} + +void +HttpServer::CloseAll(bool force) { + std::list<Connection*> connections; + for (ConnectionMap::const_iterator it = connections_.begin(); + it != connections_.end(); ++it) { + connections.push_back(it->second); + } + for (std::list<Connection*>::const_iterator it = connections.begin(); + it != connections.end(); ++it) { + (*it)->InitiateClose(force); + } +} + +HttpServer::Connection* +HttpServer::Find(int connection_id) { + ConnectionMap::iterator it = connections_.find(connection_id); + if (it == connections_.end()) + return NULL; + return it->second; +} + +void +HttpServer::Remove(int connection_id) { + ConnectionMap::iterator it = connections_.find(connection_id); + if (it == connections_.end()) { + ASSERT(false); + return; + } + Connection* connection = it->second; + connections_.erase(it); + SignalConnectionClosed(this, connection_id, connection->EndProcess()); + delete connection; +} + +/////////////////////////////////////////////////////////////////////////////// +// HttpServer::Connection +/////////////////////////////////////////////////////////////////////////////// + +HttpServer::Connection::Connection(int connection_id, HttpServer* server) + : connection_id_(connection_id), server_(server), + current_(NULL), signalling_(false), close_(false) { +} + +HttpServer::Connection::~Connection() { + delete current_; +} + +void +HttpServer::Connection::BeginProcess(StreamInterface* stream) { + base_.notify(this); + base_.attach(stream); + current_ = new HttpTransaction(connection_id_); + current_->request()->document.reset(new MemoryStream); + if (base_.mode() != HM_CONNECT) + base_.recv(current_->request()); +} + +StreamInterface* +HttpServer::Connection::EndProcess() { + base_.notify(NULL); + base_.abort(HE_DISCONNECTED); + return base_.detach(); +} + +void +HttpServer::Connection::Respond(HttpTransaction* transaction) { + ASSERT(current_ == NULL); + current_ = transaction; + if (current_->response()->begin() == current_->response()->end()) { + current_->response()->set_error(HC_INTERNAL_SERVER_ERROR); + } + bool keep_alive = HttpShouldKeepAlive(*transaction->request()); + current_->response()->setHeader(HH_CONNECTION, + keep_alive ? "Keep-Alive" : "Close", + false); + close_ = !HttpShouldKeepAlive(*transaction->response()); + base_.send(current_->response()); +} + +void +HttpServer::Connection::InitiateClose(bool force) { + if (!signalling_ && (force || (base_.mode() != HM_SEND))) { + server_->Remove(connection_id_); + } else { + close_ = true; + } +} + +// +// IHttpNotify Implementation +// + +HttpError +HttpServer::Connection::onHttpHeaderComplete(bool chunked, size_t& data_size) { + if (data_size == SIZE_UNKNOWN) { + data_size = 0; + } + return HE_NONE; +} + +void +HttpServer::Connection::onHttpComplete(HttpMode mode, HttpError err) { + if (mode == HM_SEND) { + ASSERT(current_ != NULL); + signalling_ = true; + server_->SignalHttpRequestComplete(server_, current_, err); + signalling_ = false; + if (close_) { + // Force a close + err = HE_DISCONNECTED; + } + } + if (err != HE_NONE) { + server_->Remove(connection_id_); + } else if (mode == HM_CONNECT) { + base_.recv(current_->request()); + } else if (mode == HM_RECV) { + ASSERT(current_ != NULL); + // TODO: do we need this? + //request_.document_->rewind(); + HttpTransaction* transaction = current_; + current_ = NULL; + server_->SignalHttpRequest(server_, transaction); + } else if (mode == HM_SEND) { + current_->request()->clear(true); + current_->request()->document.reset(new MemoryStream); + current_->response()->clear(true); + base_.recv(current_->request()); + } else { + ASSERT(false); + } +} + +void +HttpServer::Connection::onHttpClosed(HttpError err) { + UNUSED(err); + server_->Remove(connection_id_); +} + +/////////////////////////////////////////////////////////////////////////////// +// HttpListenServer +/////////////////////////////////////////////////////////////////////////////// + +HttpListenServer::HttpListenServer(AsyncSocket* listener) + : listener_(listener) { + listener_->SignalReadEvent.connect(this, &HttpListenServer::OnReadEvent); +} + +HttpListenServer::~HttpListenServer() { +} + +int +HttpListenServer::Listen(const SocketAddress& address) { + if ((listener_->Bind(address) != SOCKET_ERROR) && + (listener_->Listen(5) != SOCKET_ERROR)) + return 0; + return listener_->GetError(); +} + +bool +HttpListenServer::GetAddress(SocketAddress& address) { + address = listener_->GetLocalAddress(); + return !address.IsNil(); +} + +void +HttpListenServer::OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == listener_); + AsyncSocket* incoming = static_cast<AsyncSocket*>(listener_->Accept(NULL)); + if (incoming) + HandleConnection(new SocketStream(incoming)); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/httpserver.h b/Plugins/jingle/libjingle/talk/base/httpserver.h new file mode 100644 index 0000000..a09218c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/httpserver.h @@ -0,0 +1,143 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_HTTPSERVER_H__ +#define TALK_BASE_HTTPSERVER_H__ + +#include <map> +#include "talk/base/httpbase.h" + +namespace talk_base { + +class AsyncSocket; +class HttpServer; +class SocketAddress; + +////////////////////////////////////////////////////////////////////// +// HttpServer +////////////////////////////////////////////////////////////////////// + +const int HTTP_INVALID_CONNECTION_ID = 0; + +class HttpTransaction { +public: + HttpTransaction(int connection_id) : connection_id_(connection_id) { } + ~HttpTransaction() { } + + int connection_id() const { return connection_id_; } + + HttpRequestData* request() { return &request_; } + HttpResponseData* response() { return &response_; } + +private: + int connection_id_; + HttpRequestData request_; + HttpResponseData response_; +}; + +class HttpServer { +public: + HttpServer(); + virtual ~HttpServer(); + + int HandleConnection(StreamInterface* stream); + // Due to sigslot issues, we can't destroy some streams at an arbitrary time. + sigslot::signal3<HttpServer*, int, StreamInterface*> SignalConnectionClosed; + + // An HTTP request has been made, and is available in the transaction object. + // Populate the transaction's response, and then return the object via the + // Respond method. Note that during this time, ownership of the transaction + // object is transferred, so it may be passed between threads, although + // respond must be called on the server's active thread. + sigslot::signal2<HttpServer*, HttpTransaction*> SignalHttpRequest; + void Respond(HttpTransaction* transaction); + + // If you want to know when a request completes, listen to this event. + sigslot::signal3<HttpServer*, HttpTransaction*, int> + SignalHttpRequestComplete; + + // Stop processing the connection indicated by connection_id. + // Unless force is true, the server will complete sending a response that is + // in progress. + void Close(int connection_id, bool force); + void CloseAll(bool force); + +private: + class Connection : private IHttpNotify { + public: + Connection(int connection_id, HttpServer* server); + virtual ~Connection(); + + void BeginProcess(StreamInterface* stream); + StreamInterface* EndProcess(); + + void Respond(HttpTransaction* transaction); + void InitiateClose(bool force); + + // IHttpNotify Interface + virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size); + virtual void onHttpComplete(HttpMode mode, HttpError err); + virtual void onHttpClosed(HttpError err); + + int connection_id_; + HttpServer* server_; + HttpBase base_; + HttpTransaction* current_; + bool signalling_, close_; + }; + + Connection* Find(int connection_id); + void Remove(int connection_id); + + friend class Connection; + typedef std::map<int,Connection*> ConnectionMap; + + ConnectionMap connections_; + int next_connection_id_; +}; + +////////////////////////////////////////////////////////////////////// + +class HttpListenServer : public HttpServer, public sigslot::has_slots<> { +public: + HttpListenServer(AsyncSocket* listener); + virtual ~HttpListenServer(); + + int Listen(const SocketAddress& address); + bool GetAddress(SocketAddress& address); + +private: + void OnReadEvent(AsyncSocket* socket); + + AsyncSocket* listener_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_HTTPSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/icftypes.h b/Plugins/jingle/libjingle/talk/base/icftypes.h new file mode 100644 index 0000000..a88c591 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/icftypes.h @@ -0,0 +1,116 @@ +
+#pragma warning( disable: 4049 ) /* more than 64k source lines */
+
+/* this ALWAYS GENERATED file contains the definitions for the interfaces */
+
+
+ /* File created by MIDL compiler version 6.00.0347 */
+/* Compiler settings for icftypes.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
+
+
+/* verify that the <rpcndr.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+#include "rpc.h"
+#include "rpcndr.h"
+
+#ifndef __RPCNDR_H_VERSION__
+#error this stub requires an updated version of <rpcndr.h>
+#endif // __RPCNDR_H_VERSION__
+
+
+#ifndef __icftypes_h__
+#define __icftypes_h__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+/* Forward Declarations */
+
+/* header files for imported files */
+#include "wtypes.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void * __RPC_USER MIDL_user_allocate(size_t);
+void __RPC_USER MIDL_user_free( void * );
+
+/* interface __MIDL_itf_icftypes_0000 */
+/* [local] */
+
+typedef
+enum NET_FW_POLICY_TYPE_
+ { NET_FW_POLICY_GROUP = 0,
+ NET_FW_POLICY_LOCAL = NET_FW_POLICY_GROUP + 1,
+ NET_FW_POLICY_EFFECTIVE = NET_FW_POLICY_LOCAL + 1,
+ NET_FW_POLICY_TYPE_MAX = NET_FW_POLICY_EFFECTIVE + 1
+ } NET_FW_POLICY_TYPE;
+
+typedef
+enum NET_FW_PROFILE_TYPE_
+ { NET_FW_PROFILE_DOMAIN = 0,
+ NET_FW_PROFILE_STANDARD = NET_FW_PROFILE_DOMAIN + 1,
+ NET_FW_PROFILE_CURRENT = NET_FW_PROFILE_STANDARD + 1,
+ NET_FW_PROFILE_TYPE_MAX = NET_FW_PROFILE_CURRENT + 1
+ } NET_FW_PROFILE_TYPE;
+
+typedef
+enum NET_FW_IP_VERSION_
+ { NET_FW_IP_VERSION_V4 = 0,
+ NET_FW_IP_VERSION_V6 = NET_FW_IP_VERSION_V4 + 1,
+ NET_FW_IP_VERSION_ANY = NET_FW_IP_VERSION_V6 + 1,
+ NET_FW_IP_VERSION_MAX = NET_FW_IP_VERSION_ANY + 1
+ } NET_FW_IP_VERSION;
+
+typedef
+enum NET_FW_SCOPE_
+ { NET_FW_SCOPE_ALL = 0,
+ NET_FW_SCOPE_LOCAL_SUBNET = NET_FW_SCOPE_ALL + 1,
+ NET_FW_SCOPE_CUSTOM = NET_FW_SCOPE_LOCAL_SUBNET + 1,
+ NET_FW_SCOPE_MAX = NET_FW_SCOPE_CUSTOM + 1
+ } NET_FW_SCOPE;
+
+typedef
+enum NET_FW_IP_PROTOCOL_
+ { NET_FW_IP_PROTOCOL_TCP = 6,
+ NET_FW_IP_PROTOCOL_UDP = 17
+ } NET_FW_IP_PROTOCOL;
+
+typedef
+enum NET_FW_SERVICE_TYPE_
+ { NET_FW_SERVICE_FILE_AND_PRINT = 0,
+ NET_FW_SERVICE_UPNP = NET_FW_SERVICE_FILE_AND_PRINT + 1,
+ NET_FW_SERVICE_REMOTE_DESKTOP = NET_FW_SERVICE_UPNP + 1,
+ NET_FW_SERVICE_NONE = NET_FW_SERVICE_REMOTE_DESKTOP + 1,
+ NET_FW_SERVICE_TYPE_MAX = NET_FW_SERVICE_NONE + 1
+ } NET_FW_SERVICE_TYPE;
+
+
+
+extern RPC_IF_HANDLE __MIDL_itf_icftypes_0000_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_icftypes_0000_v0_0_s_ifspec;
+
+/* Additional Prototypes for ALL interfaces */
+
+/* end of Additional Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/Plugins/jingle/libjingle/talk/base/linked_ptr.h b/Plugins/jingle/libjingle/talk/base/linked_ptr.h new file mode 100644 index 0000000..8d5ce49 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/linked_ptr.h @@ -0,0 +1,115 @@ +/* + * linked_ptr - simple reference linked pointer + * (like reference counting, just using a linked list of the references + * instead of their count.) + * + * The implementation stores three pointers for every linked_ptr, but + * does not allocate anything on the free store. + */ + +#ifndef TALK_BASE_LINKED_PTR_H__ +#define TALK_BASE_LINKED_PTR_H__ + +namespace talk_base { + +/* For ANSI-challenged compilers, you may want to #define + * NO_MEMBER_TEMPLATES, explicit or mutable */ +#define NO_MEMBER_TEMPLATES + +template <class X> class linked_ptr +{ +public: + +#ifndef NO_MEMBER_TEMPLATES +# define TEMPLATE_FUNCTION template <class Y> + TEMPLATE_FUNCTION friend class linked_ptr<Y>; +#else +# define TEMPLATE_FUNCTION + typedef X Y; +#endif + + typedef X element_type; + + explicit linked_ptr(X* p = 0) throw() + : itsPtr(p) {itsPrev = itsNext = this;} + ~linked_ptr() + {release();} + linked_ptr(const linked_ptr& r) throw() + {acquire(r);} + linked_ptr& operator=(const linked_ptr& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } + +#ifndef NO_MEMBER_TEMPLATES + template <class Y> friend class linked_ptr<Y>; + template <class Y> linked_ptr(const linked_ptr<Y>& r) throw() + {acquire(r);} + template <class Y> linked_ptr& operator=(const linked_ptr<Y>& r) + { + if (this != &r) { + release(); + acquire(r); + } + return *this; + } +#endif // NO_MEMBER_TEMPLATES + + X& operator*() const throw() {return *itsPtr;} + X* operator->() const throw() {return itsPtr;} + X* get() const throw() {return itsPtr;} + bool unique() const throw() {return itsPrev ? itsPrev==this : true;} + +private: + X* itsPtr; + mutable const linked_ptr* itsPrev; + mutable const linked_ptr* itsNext; + + void acquire(const linked_ptr& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast<linked_ptr<X>*>(&r))->itsNext = this; +#endif + } + +#ifndef NO_MEMBER_TEMPLATES + template <class Y> void acquire(const linked_ptr<Y>& r) throw() + { // insert this to the list + itsPtr = r.itsPtr; + itsNext = r.itsNext; + itsNext->itsPrev = this; + itsPrev = &r; +#ifndef mutable + r.itsNext = this; +#else // for ANSI-challenged compilers + (const_cast<linked_ptr<X>*>(&r))->itsNext = this; +#endif + } +#endif // NO_MEMBER_TEMPLATES + + void release() + { // erase this from the list, delete if unique + if (unique()) delete itsPtr; + else { + itsPrev->itsNext = itsNext; + itsNext->itsPrev = itsPrev; + itsPrev = itsNext = 0; + } + itsPtr = 0; + } +}; + +} // namespace talk_base + +#endif // TALK_BASE_LINKED_PTR_H__ + diff --git a/Plugins/jingle/libjingle/talk/base/logging.cc b/Plugins/jingle/libjingle/talk/base/logging.cc new file mode 100644 index 0000000..2a7fbe3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/logging.cc @@ -0,0 +1,349 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef WIN32 +#include <windows.h> +#define snprintf _snprintf +#undef ERROR // wingdi.h +#endif + +#include <iostream> +#include <iomanip> + +#include "talk/base/logging.h" +#include "talk/base/stream.h" +#include "talk/base/stringencode.h" +#include "talk/base/time.h" + +namespace talk_base { + +///////////////////////////////////////////////////////////////////////////// +// Constant Labels +///////////////////////////////////////////////////////////////////////////// + +const char * FindLabel(int value, const talk_base::ConstantLabel entries[]) { + for (int i=0; entries[i].label; ++i) { + if (value == entries[i].value) { + return entries[i].label; + } + } + return 0; +} + +std::string ErrorName(int err, const talk_base::ConstantLabel * err_table) { + const char * value = 0; + if (err == 0) + return "No error"; + + if (err_table != 0) { + if (const char * value = FindLabel(err, err_table)) + return value; + } + + char buffer[16]; + snprintf(buffer, sizeof(buffer), "0x%08lx", err); + return buffer; +} + +///////////////////////////////////////////////////////////////////////////// +// LogMessage +///////////////////////////////////////////////////////////////////////////// + +#if _DEBUG +static const int LOG_DEFAULT = LS_INFO; +#else // !_DEBUG +static const int LOG_DEFAULT = LogMessage::NO_LOGGING; +#endif // !_DEBUG + +// By default, release builds don't log, debug builds at info level +int LogMessage::min_sev_ = LOG_DEFAULT; +int LogMessage::dbg_sev_ = LOG_DEFAULT; + +// No file logging by default +int LogMessage::stream_sev_ = NO_LOGGING; + +// Don't bother printing context for the ubiquitous INFO log messages +int LogMessage::ctx_sev_ = LS_WARNING; + +// stream_ defaults to NULL +// Note: we explicitly do not clean this up, because of the uncertain ordering +// of destructors at program exit. Let the person who sets the stream trigger +// cleanup by setting to NULL, or let it leak (safe at program exit). +StreamInterface* LogMessage::stream_; + +// Boolean options default to false (0) +bool LogMessage::thread_, LogMessage::timestamp_; + +// Program start time +uint32 LogMessage::start_ = StartTime(); + +// if we're in diagnostic mode, we'll be explicitly set that way. default to false +bool LogMessage::is_diagnostic_mode_ = false; + +LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, + LogErrorContext err_ctx, int err, const char* module) + : severity_(sev) { + if (timestamp_) { + uint32 time = TimeDiff(Time(), start_); + print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000) + << ":" << std::setw(3) << (time % 1000) << std::setfill(' ') + << "] "; + } + + if (thread_) { +#ifdef WIN32 + DWORD id = GetCurrentThreadId(); + print_stream_ << "[" << std::hex << id << std::dec << "] "; +#endif // WIN32 + } + + if (severity_ >= ctx_sev_) { + print_stream_ << Describe(sev) << "(" << DescribeFile(file) + << ":" << line << "): "; + } + + if (err_ctx != ERRCTX_NONE) { + std::ostringstream tmp; + tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]"; + switch (err_ctx) { + case ERRCTX_ERRNO: + tmp << " " << strerror(err); + break; + #ifdef WIN32 + case ERRCTX_HRESULT: { + char msgbuf[256]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM; + HMODULE hmod = GetModuleHandleA(module); + if (hmod) + flags |= FORMAT_MESSAGE_FROM_HMODULE; + if (DWORD len = FormatMessageA( + flags, hmod, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) { + while ((len > 0) && + isspace(static_cast<unsigned char>(msgbuf[len-1]))) { + msgbuf[--len] = 0; + } + tmp << " " << msgbuf; + } + break; } + #endif // WIN32 + default: + break; + } + extra_ = tmp.str(); + } +} + +LogMessage::~LogMessage() { + if (!extra_.empty()) + print_stream_ << " : " << extra_; + print_stream_ << std::endl; + const std::string& str = print_stream_.str(); + + if (severity_ >= dbg_sev_) { + bool log_to_stderr = true; +#ifdef WIN32 + static bool debugger_present = (IsDebuggerPresent() != FALSE); + if (debugger_present) { + log_to_stderr = false; + OutputDebugStringA(str.c_str()); + } + if (log_to_stderr) { + // This handles dynamically allocated consoles, too. + if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { + log_to_stderr = false; + unsigned long written; + ::WriteFile(error_handle, str.data(), str.size(), &written, 0); + } + } +#endif // WIN32 + if (log_to_stderr) { + std::cerr << str; + std::cerr.flush(); + } + } + + if (severity_ >= stream_sev_) { + // If write isn't fully successful, what are we going to do, log it? :) + stream_->WriteAll(str.data(), str.size(), NULL, NULL); + } +} + +void LogMessage::LogContext(int min_sev) { + ctx_sev_ = min_sev; +} + +void LogMessage::LogThreads(bool on) { + thread_ = on; +} + +void LogMessage::LogTimestamps(bool on) { + timestamp_ = on; +} + +void LogMessage::ResetTimestamps() { + start_ = Time(); +} + +void LogMessage::LogToDebug(int min_sev) { + dbg_sev_ = min_sev; + min_sev_ = _min(dbg_sev_, stream_sev_); +} + +void LogMessage::LogToStream(StreamInterface* stream, int min_sev) { + delete stream_; + stream_ = stream; + stream_sev_ = (stream_ == 0) ? NO_LOGGING : min_sev; + min_sev_ = _min(dbg_sev_, stream_sev_); +} + +const char* LogMessage::Describe(LoggingSeverity sev) { + switch (sev) { + case LS_SENSITIVE: return "Sensitive"; + case LS_VERBOSE: return "Verbose"; + case LS_INFO: return "Info"; + case LS_WARNING: return "Warning"; + case LS_ERROR: return "Error"; + default: return "<unknown>"; + } +} + +const char* LogMessage::DescribeFile(const char* file) { + const char* end1 = ::strrchr(file, '/'); + const char* end2 = ::strrchr(file, '\\'); + if (!end1 && !end2) + return file; + else + return (end1 > end2) ? end1 + 1 : end2 + 1; +} + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const char * data, size_t len, bool hex_mode, + LogMultilineState* state) { + if (!LOG_CHECK_LEVEL_V(level)) + return; + + const char * direction = (input ? " << " : " >> "); + if (hex_mode) { + const size_t LINE_SIZE = 24; + char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1]; + while (len > 0) { + memset(asc_line, ' ', sizeof(asc_line)); + memset(hex_line, ' ', sizeof(hex_line)); + size_t line_len = _min(len, LINE_SIZE); + for (size_t i=0; i<line_len; ++i) { + unsigned char ch = static_cast<unsigned char>(data[i]); + asc_line[i] = isprint(ch) ? data[i] : '.'; + hex_line[i*2 + i/4] = hex_encode(ch >> 4); + hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf); + } + asc_line[sizeof(asc_line)-1] = 0; + hex_line[sizeof(hex_line)-1] = 0; + LOG_V(level) << label << direction + << asc_line << " " << hex_line << " "; + data += line_len; + len -= line_len; + } + return; + } + + size_t consecutive_unprintable = state ? state->unprintable_count_ : 0; + + std::string str(data, len); + while (!str.empty()) { + size_t line_end_length = 0; + std::string::size_type pos = str.find('\n'); + std::string substr = str; + if (pos == std::string::npos) { + substr = str; + str.clear(); + } else if ((pos > 0) && (str[pos-1] == '\r')) { + line_end_length = 2; + substr = str.substr(0, pos - 1); + str = str.substr(pos + 1); + } else { + line_end_length = 1; + substr = str.substr(0, pos); + str = str.substr(pos + 1); + } + + // Any lines which consist entirely of ascii characters are printed. Other + // lines are considered binary, and we just count the number of bytes. + // This algorithm should be very compatible with HTTP transfers of binary + // data. + bool is_ascii = true, is_whitespace = true; + for (size_t i=0; i<substr.size(); ++i) { + unsigned char ch = static_cast<unsigned char>(substr[i]); + if (!isprint(ch)) { + is_ascii = false; + break; + } else if (!isspace(static_cast<unsigned char>(ch))) { + is_whitespace = false; + } + } + // Treat an empty line following binary data as binary. + if (is_whitespace && consecutive_unprintable) { + is_ascii = false; + } + if (!is_ascii) { + consecutive_unprintable += substr.size() + line_end_length; + } + if (consecutive_unprintable && (is_ascii || str.empty())) { + LOG_V(level) << label << direction << "## " << consecutive_unprintable + << " consecutive unprintable ##"; + } + if (is_ascii) { + consecutive_unprintable = 0; + } else { + continue; + } + + // Filter out any private data + std::string::size_type pos_private = substr.find("Email"); + if (pos_private == std::string::npos) { + pos_private = substr.find("Passwd"); + } + if (pos_private == std::string::npos) { + LOG_V(level) << label << direction << substr; + } else { + LOG_V(level) << label << direction << "## omitted for privacy ##"; + } + } + + if (state) { + state->unprintable_count_ = consecutive_unprintable; + } +} + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/logging.h b/Plugins/jingle/libjingle/talk/base/logging.h new file mode 100644 index 0000000..a4686b7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/logging.h @@ -0,0 +1,296 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// LOG(...) an ostream target that can be used to send formatted +// output to a variety of logging targets, such as debugger console, stderr, +// file, or any StreamInterface. +// The severity level passed as the first argument to the the LOGging +// functions is used as a filter, to limit the verbosity of the logging. +// Static members of LogMessage documented below are used to control the +// verbosity and target of the output. +// There are several variations on the LOG macro which facilitate logging +// of common error conditions, detailed below. + +#ifndef TALK_BASE_LOGGING_H__ +#define TALK_BASE_LOGGING_H__ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sstream> +#include "talk/base/basictypes.h" + +namespace talk_base { + +class StreamInterface; + +/////////////////////////////////////////////////////////////////////////////// +// ConstantLabel can be used to easily generate string names from constant +// values. This can be useful for logging descriptive names of error messages. +// Usage: +// const ConstantLabel LIBRARY_ERRORS[] = { +// KLABEL(SOME_ERROR), +// KLABEL(SOME_OTHER_ERROR), +// ... +// LASTLABEL +// } +// +// int err = LibraryFunc(); +// LOG(LS_ERROR) << "LibraryFunc returned: " +// << ErrorName(err, LIBRARY_ERRORS); + +struct ConstantLabel { int value; const char * label; }; +#define KLABEL(x) { x, #x } +#define TLABEL(x,y) { x, y } +#define LASTLABEL { 0, 0 } + +const char * FindLabel(int value, const ConstantLabel entries[]); +std::string ErrorName(int err, const ConstantLabel* err_table); + +////////////////////////////////////////////////////////////////////// + +// Note that the non-standard LoggingSeverity aliases exist because they are +// still in broad use. The meanings of the levels are: +// LS_SENSITIVE: Information which should only be logged with the consent +// of the user, due to privacy concerns. +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +enum LoggingSeverity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, + INFO = LS_INFO, + WARNING = LS_WARNING, + LERROR = LS_ERROR }; + +// LogErrorContext assists in interpreting the meaning of an error value. +// ERRCTX_ERRNO: the value was read from global 'errno' +// ERRCTX_HRESULT: the value is a Windows HRESULT +enum LogErrorContext { ERRCTX_NONE, ERRCTX_ERRNO, ERRCTX_HRESULT }; + +class LogMessage { + public: + LogMessage(const char* file, int line, LoggingSeverity sev, + LogErrorContext err_ctx = ERRCTX_NONE, int err = 0, + const char* module = NULL); + ~LogMessage(); + + static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); } + std::ostream& stream() { return print_stream_; } + + enum { NO_LOGGING = LS_ERROR + 1 }; + + // These are attributes which apply to all logging channels + // LogContext: Display the file and line number of the message + static void LogContext(int min_sev); + // LogThreads: Display the thread identifier of the current thread + static void LogThreads(bool on = true); + // LogTimestamps: Display the elapsed time of the program + static void LogTimestamps(bool on = true); + + // Timestamps begin with program execution, but can be reset with this + // function for measuring the duration of an activity, or to synchronize + // timestamps between multiple instances. + static void ResetTimestamps(); + + // These are the available logging channels + // Debug: Debug console on Windows, otherwise stderr + static void LogToDebug(int min_sev); + static int GetLogToDebug() { return dbg_sev_; } + // Stream: Any non-blocking stream interface. LogMessage takes ownership of + // the stream. + static void LogToStream(StreamInterface* stream, int min_sev); + static int GetLogToStream() { return stream_sev_; } + + // Testing against MinLogSeverity allows code to avoid potentially expensive + // logging operations by pre-checking the logging level. + static int GetMinLogSeverity() { return min_sev_; } + + static void SetDiagnosticMode(bool f) { is_diagnostic_mode_ = f; } + static bool IsDiagnosticMode() { return is_diagnostic_mode_; } + + private: + // These assist in formatting some parts of the debug output. + static const char* Describe(LoggingSeverity sev); + static const char* DescribeFile(const char* file); + + // The ostream that buffers the formatted message before output + std::ostringstream print_stream_; + + // The severity level of this message + LoggingSeverity severity_; + + // String data generated in the constructor, that should be appended to + // the message before output. + std::string extra_; + + // dbg_sev_ and stream_sev_ are the thresholds for those output targets + // min_sev_ is the minimum (most verbose) of those levels, and is used + // as a short-circuit in the logging macros to identify messages that won't + // be logged. + // ctx_sev_ is the minimum level at which file context is displayed + static int min_sev_, dbg_sev_, stream_sev_, ctx_sev_; + + // The output stream, if any + static StreamInterface * stream_; + + // Flags for formatting options + static bool thread_, timestamp_; + + // The timestamp at which logging started. + static uint32 start_; + + // are we in diagnostic mode (as defined by the app)? + static bool is_diagnostic_mode_; + + DISALLOW_EVIL_CONSTRUCTORS(LogMessage); +}; + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +class LogMultilineState { +public: + size_t unprintable_count_; + LogMultilineState() : unprintable_count_(0) { } +}; + +// When possible, pass optional state variable to track various data across +// multiple calls to LogMultiline. Otherwise, pass NULL. +void LogMultiline(LoggingSeverity level, const char* label, bool input, + const char * data, size_t len, bool hex_mode, + LogMultilineState* state); + +////////////////////////////////////////////////////////////////////// +// Macros which automatically disable logging when LOGGING == 0 +////////////////////////////////////////////////////////////////////// + +// If LOGGING is not explicitly defined, default to enabled in debug mode +#if !defined(LOGGING) +#if defined(_DEBUG) && !defined(NDEBUG) +#define LOGGING 1 +#else +#define LOGGING 0 +#endif +#endif // !defined(LOGGING) + +#ifndef LOG +#if LOGGING + +#define LOG(sev) \ + if (talk_base::LogMessage::Loggable(talk_base::sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev).stream() + +// The _V version is for when a variable is passed in. It doesn't do the +// namespace concatination. +#define LOG_V(sev) \ + if (talk_base::LogMessage::Loggable(sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, sev).stream() + +// The _F version prefixes the message with the current function name. +#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " + +// LOG_CHECK_LEVEL can be used as a test before performing expensive or +// sensitive operations whose sole purpose is to output logging data at the +// desired level. +#define LOG_CHECK_LEVEL(sev) \ + talk_base::LogCheckLevel(talk_base::sev) +#define LOG_CHECK_LEVEL_V(sev) \ + talk_base::LogCheckLevel(sev) +inline bool LogCheckLevel(LoggingSeverity sev) { + return (LogMessage::GetMinLogSeverity() <= sev); +} + +// PLOG and LOG_ERR attempt to provide a string description of an errno derived +// error. LOG_ERR reads errno directly, so care must be taken to call it before +// errno is reset. +#define PLOG(sev, err) \ + if (talk_base::LogMessage::Loggable(talk_base::sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \ + talk_base::ERRCTX_ERRNO, err).stream() +#define LOG_ERR(sev) \ + if (talk_base::LogMessage::Loggable(sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \ + talk_base::ERRCTX_ERRNO, errno).stream() + +// LOG_GLE(M) attempt to provide a string description of the HRESULT returned +// by GetLastError. The second variant allows searching of a dll's string +// table for the error description. +#ifdef WIN32 +#define LOG_GLE(sev) \ + if (talk_base::LogMessage::Loggable(talk_base::sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \ + talk_base::ERRCTX_HRESULT, GetLastError()).stream() +#define LOG_GLEM(sev, mod) \ + if (talk_base::LogMessage::Loggable(talk_base::sev)) \ + talk_base::LogMessage(__FILE__, __LINE__, talk_base::sev, \ + talk_base::ERRCTX_HRESULT, GetLastError(), mod) \ + .stream() +#endif // WIN32 + +// TODO: Add an "assert" wrapper that logs in the same manner. + +#else // !LOGGING + +// Hopefully, the compiler will optimize away some of this code. +// Note: syntax of "1 ? (void)0 : LogMessage" was causing errors in g++, +// converted to "while (false)" +#define LOG(sev) \ + while (false)talk_base:: LogMessage(NULL, 0, talk_base::sev).stream() +#define LOG_V(sev) \ + while (false) talk_base::LogMessage(NULL, 0, sev).stream() +#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " +#define LOG_CHECK_LEVEL(sev) \ + false +#define LOG_CHECK_LEVEL_V(sev) \ + false +#define PLOG(sev, err) \ + while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \ + talk_base::ERRCTX_ERRNO, 0).stream() +#define LOG_ERR(sev) \ + while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \ + talk_base::ERRCTX_ERRNO, 0).stream() +#ifdef WIN32 +#define LOG_GLE(sev) \ + while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \ + talk_base::ERRCTX_HRESULT, 0).stream() +#define LOG_GLEM(sev, mod) \ + while (false) talk_base::LogMessage(NULL, 0, talk_base::sev, \ + talk_base::ERRCTX_HRESULT, 0).stream() +#endif // WIN32 + +#endif // !LOGGING +#endif // LOG + +////////////////////////////////////////////////////////////////////// + +} // talk_base + +#endif // TALK_BASE_LOGGING_H__ diff --git a/Plugins/jingle/libjingle/talk/base/md5.h b/Plugins/jingle/libjingle/talk/base/md5.h new file mode 100644 index 0000000..ec458d1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/md5.h @@ -0,0 +1,45 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#ifndef TALK_BASE_MD5_H__ +#define TALK_BASE_MD5_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long unsigned int uint32; +typedef struct MD5Context MD5_CTX; + +#define md5byte unsigned char + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + uint32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +#ifdef __cplusplus +}; +#endif + +#endif // TALK_BASE_MD5_H__ diff --git a/Plugins/jingle/libjingle/talk/base/md5c.c b/Plugins/jingle/libjingle/talk/base/md5c.c new file mode 100644 index 0000000..eb2c034 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/md5c.c @@ -0,0 +1,256 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#include <string.h> /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = (unsigned char*)(ctx->in) + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; + ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif diff --git a/Plugins/jingle/libjingle/talk/base/messagequeue.cc b/Plugins/jingle/libjingle/talk/base/messagequeue.cc new file mode 100644 index 0000000..64f57cc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/messagequeue.cc @@ -0,0 +1,360 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#ifdef POSIX +extern "C" { +#include <sys/time.h> +} +#endif + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/messagequeue.h" +#include "talk/base/physicalsocketserver.h" + + +namespace talk_base { + +const uint32 kMaxMsgLatency = 150; // 150 ms + +//------------------------------------------------------------------ +// MessageQueueManager + +MessageQueueManager* MessageQueueManager::instance_; + +MessageQueueManager* MessageQueueManager::Instance() { + // Note: This is not thread safe, but it is first called before threads are + // spawned. + if (!instance_) + instance_ = new MessageQueueManager; + return instance_; +} + +MessageQueueManager::MessageQueueManager() { +} + +MessageQueueManager::~MessageQueueManager() { +} + +void MessageQueueManager::Add(MessageQueue *message_queue) { + // MessageQueueManager methods should be non-reentrant, so we + // ASSERT that is the case. + ASSERT(!crit_.CurrentThreadIsOwner()); + CritScope cs(&crit_); + message_queues_.push_back(message_queue); +} + +void MessageQueueManager::Remove(MessageQueue *message_queue) { + ASSERT(!crit_.CurrentThreadIsOwner()); // See note above. + CritScope cs(&crit_); + std::vector<MessageQueue *>::iterator iter; + iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue); + if (iter != message_queues_.end()) + message_queues_.erase(iter); +} + +void MessageQueueManager::Clear(MessageHandler *handler) { + ASSERT(!crit_.CurrentThreadIsOwner()); // See note above. + CritScope cs(&crit_); + std::vector<MessageQueue *>::iterator iter; + for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++) + (*iter)->Clear(handler); +} + +//------------------------------------------------------------------ +// MessageQueue + +MessageQueue::MessageQueue(SocketServer* ss) + : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false), active_(false) { + if (!ss_) { + new_ss = true; + ss_ = new PhysicalSocketServer(); + } +} + +MessageQueue::~MessageQueue() { + if (active_) { + MessageQueueManager::Instance()->Remove(this); + Clear(NULL); + } + if (new_ss) + delete ss_; +} + +void MessageQueue::set_socketserver(SocketServer* ss) { + if (new_ss) + delete ss_; + new_ss = false; + ss_ = ss; +} + +void MessageQueue::Stop() { + fStop_ = true; + ss_->WakeUp(); +} + +bool MessageQueue::IsStopping() { + return fStop_; +} + +void MessageQueue::Restart() { + fStop_ = false; +} + +bool MessageQueue::Peek(Message *pmsg, int cmsWait) { + if (fPeekKeep_) { + *pmsg = msgPeek_; + return true; + } + if (!Get(pmsg, cmsWait)) + return false; + msgPeek_ = *pmsg; + fPeekKeep_ = true; + return true; +} + +bool MessageQueue::Get(Message *pmsg, int cmsWait) { + // Return and clear peek if present + // Always return the peek if it exists so there is Peek/Get symmetry + + if (fPeekKeep_) { + *pmsg = msgPeek_; + fPeekKeep_ = false; + return true; + } + + // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch + + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = Time(); + uint32 msCurrent = msStart; + while (true) { + // Check for sent messages + + ReceiveSends(); + + // Check queues + + int cmsDelayNext = kForever; + { + CritScope cs(&crit_); + + // Check for delayed messages that have been triggered + // Calc the next trigger too + + while (!dmsgq_.empty()) { + if (msCurrent < dmsgq_.top().msTrigger_) { + cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent; + break; + } + msgq_.push(dmsgq_.top().msg_); + dmsgq_.pop(); + } + + // Check for posted events + + while (!msgq_.empty()) { + *pmsg = msgq_.front(); + if (pmsg->ts_sensitive) { + long delay = TimeDiff(msCurrent, pmsg->ts_sensitive); + if (delay > 0) { + LOG_F(LS_WARNING) << "id: " << pmsg->message_id << " delay: " + << (delay + kMaxMsgLatency) << "ms"; + } + } + msgq_.pop(); + if (MQID_DISPOSE == pmsg->message_id) { + ASSERT(NULL == pmsg->phandler); + delete pmsg->pdata; + continue; + } + return true; + } + } + + if (fStop_) + break; + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == kForever) { + cmsNext = cmsDelayNext; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext)) + cmsNext = cmsDelayNext; + } + + // Wait and multiplex in the meantime + ss_->Wait(cmsNext, true); + + // If the specified timeout expired, return + + msCurrent = Time(); + cmsElapsed = msCurrent - msStart; + if (cmsWait != kForever) { + if (cmsElapsed >= cmsWait) + return false; + } + } + return false; +} + +void MessageQueue::ReceiveSends() { +} + +void MessageQueue::Post(MessageHandler *phandler, uint32 id, + MessageData *pdata, bool time_sensitive) { + if (fStop_) + return; + + // Keep thread safe + // Add the message to the end of the queue + // Signal for the multiplexer to return + + CritScope cs(&crit_); + EnsureActive(); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (time_sensitive) { + msg.ts_sensitive = Time() + kMaxMsgLatency; + } + msgq_.push(msg); + ss_->WakeUp(); +} + +void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id, MessageData *pdata) { + if (fStop_) + return; + + // Keep thread safe + // Add to the priority queue. Gets sorted soonest first. + // Signal for the multiplexer to return. + + CritScope cs(&crit_); + EnsureActive(); + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + dmsgq_.push(DelayedMessage(cmsDelay, &msg)); + ss_->WakeUp(); +} + +int MessageQueue::GetDelay() { + CritScope cs(&crit_); + + if (!msgq_.empty()) + return 0; + + if (!dmsgq_.empty()) { + int delay = dmsgq_.top().msTrigger_ - Time(); + if (delay < 0) + delay = 0; + return delay; + } + + return kForever; +} + +void MessageQueue::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages with phandler + + if (fPeekKeep_) { + if (phandler == NULL || msgPeek_.phandler == phandler) { + if (id == MQID_ANY || msgPeek_.message_id == id) { + delete msgPeek_.pdata; + fPeekKeep_ = false; + } + } + } + + // Remove from ordered message queue + + size_t c = msgq_.size(); + while (c-- != 0) { + Message msg = msgq_.front(); + msgq_.pop(); + if (phandler != NULL && msg.phandler != phandler) { + msgq_.push(msg); + } else { + if (id == MQID_ANY || msg.message_id == id) { + delete msg.pdata; + } else { + msgq_.push(msg); + } + } + } + + // Remove from priority queue. Not directly iterable, so use this approach + + std::queue<DelayedMessage> dmsgs; + while (!dmsgq_.empty()) { + DelayedMessage dmsg = dmsgq_.top(); + dmsgq_.pop(); + if (phandler != NULL && dmsg.msg_.phandler != phandler) { + dmsgs.push(dmsg); + } else { + if (id == MQID_ANY || dmsg.msg_.message_id == id) { + delete dmsg.msg_.pdata; + } else { + dmsgs.push(dmsg); + } + } + } + while (!dmsgs.empty()) { + dmsgq_.push(dmsgs.front()); + dmsgs.pop(); + } +} + +void MessageQueue::Dispatch(Message *pmsg) { + pmsg->phandler->OnMessage(pmsg); +} + +void MessageQueue::EnsureActive() { + ASSERT(crit_.CurrentThreadIsOwner()); + if (!active_) { + active_ = true; + MessageQueueManager::Instance()->Add(this); + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/messagequeue.h b/Plugins/jingle/libjingle/talk/base/messagequeue.h new file mode 100644 index 0000000..ce79700 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/messagequeue.h @@ -0,0 +1,207 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_MESSAGEQUEUE_H__ +#define TALK_BASE_MESSAGEQUEUE_H__ + +#include <vector> +#include <queue> +#include <algorithm> +#include "talk/base/basictypes.h" +#include "talk/base/criticalsection.h" +#include "talk/base/socketserver.h" +#include "talk/base/time.h" + +namespace talk_base { + +struct Message; +class MessageQueue; +class MessageHandler; + +// MessageQueueManager does cleanup of of message queues + +class MessageQueueManager { +public: + static MessageQueueManager* Instance(); + + void Add(MessageQueue *message_queue); + void Remove(MessageQueue *message_queue); + void Clear(MessageHandler *handler); + +private: + MessageQueueManager(); + ~MessageQueueManager(); + + static MessageQueueManager* instance_; + // This list contains 'active' MessageQueues. + std::vector<MessageQueue *> message_queues_; + CriticalSection crit_; +}; + +// Messages get dispatched to a MessageHandler + +class MessageHandler { +public: + virtual ~MessageHandler() { + MessageQueueManager::Instance()->Clear(this); + } + + virtual void OnMessage(Message *pmsg) = 0; +}; + +// Derive from this for specialized data +// App manages lifetime, except when messages are purged + +class MessageData { +public: + MessageData() {} + virtual ~MessageData() {} +}; + +template <class T> +class TypedMessageData : public MessageData { +public: + TypedMessageData(const T& data) : data_(data) { } + const T& data() const { return data_; } + T& data() { return data_; } +private: + T data_; +}; + +template<class T> +inline MessageData* WrapMessageData(const T& data) { + return new TypedMessageData<T>(data); +} + +template<class T> +inline const T& UseMessageData(MessageData* data) { + return static_cast< TypedMessageData<T>* >(data)->data(); +} + +template<class T> +class DisposeData : public MessageData { +public: + DisposeData(T* data) : data_(data) { } + virtual ~DisposeData() { delete data_; } +private: + T* data_; +}; + +const uint32 MQID_ANY = static_cast<uint32>(-1); +const uint32 MQID_DISPOSE = static_cast<uint32>(-2); + +// No destructor + +struct Message { + Message() { + memset(this, 0, sizeof(*this)); + } + MessageHandler *phandler; + uint32 message_id; + MessageData *pdata; + uint32 ts_sensitive; +}; + +// DelayedMessage goes into a priority queue, sorted by trigger time + +class DelayedMessage { +public: + DelayedMessage(int cmsDelay, Message *pmsg) { + cmsDelay_ = cmsDelay; + msTrigger_ = GetMillisecondCount() + cmsDelay; + msg_ = *pmsg; + } + + bool operator< (const DelayedMessage& dmsg) const { + return dmsg.msTrigger_ < msTrigger_; + } + + int cmsDelay_; // for debugging + uint32 msTrigger_; + Message msg_; +}; + +class MessageQueue { +public: + MessageQueue(SocketServer* ss = 0); + virtual ~MessageQueue(); + + SocketServer* socketserver() { return ss_; } + void set_socketserver(SocketServer* ss); + + // Note: The behavior of MessageQueue has changed. When a MQ is stopped, + // futher Posts and Sends will fail. However, any pending Sends and *ready* + // Posts (as opposed to unexpired delayed Posts) will be delivered before + // Get (or Peek) returns false. By guaranteeing delivery of those messages, + // we eliminate the race condition when an MessageHandler and MessageQueue + // may be destroyed independently of each other. + + virtual void Stop(); + virtual bool IsStopping(); + virtual void Restart(); + + // Get() will process I/O until: + // 1) A message is available (returns true) + // 2) cmsWait seconds have elapsed (returns false) + // 3) Stop() is called (returns false) + virtual bool Get(Message *pmsg, int cmsWait = kForever); + virtual bool Peek(Message *pmsg, int cmsWait = 0); + virtual void Post(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL, bool time_sensitive = false); + virtual void PostDelayed(int cmsDelay, MessageHandler *phandler, + uint32 id = 0, MessageData *pdata = NULL); + virtual void Clear(MessageHandler *phandler, uint32 id = MQID_ANY); + virtual void Dispatch(Message *pmsg); + virtual void ReceiveSends(); + virtual int GetDelay(); + + // Internally posts a message which causes the doomed object to be deleted + template<class T> void Dispose(T* doomed) { + if (doomed) { + Post(NULL, MQID_DISPOSE, new talk_base::DisposeData<T>(doomed)); + } + } + +protected: + void EnsureActive(); + + SocketServer* ss_; + bool new_ss; + bool fStop_; + bool fPeekKeep_; + Message msgPeek_; + // A message queue is active if it has ever had a message posted to it. + // This also corresponds to being in MessageQueueManager's global list. + bool active_; + std::queue<Message> msgq_; + std::priority_queue<DelayedMessage> dmsgq_; + CriticalSection crit_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_MESSAGEQUEUE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/nat_unittest.cc b/Plugins/jingle/libjingle/talk/base/nat_unittest.cc new file mode 100644 index 0000000..23c122b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/nat_unittest.cc @@ -0,0 +1,223 @@ +#include <string> +#include <cstring> +#include <iostream> +#include <cassert> + +#include "talk/base/natserver.h" +#include "talk/base/testclient.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/virtualsocketserver.h" +#include "talk/base/natsocketfactory.h" +#include "talk/base/host.h" + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace talk_base; + +#define CHECK(arg) Check(arg, #arg) + +void Check(int result, const char* desc) { + if (result < 0) { + std::cerr << desc << ": " << std::strerror(errno) << std::endl; + exit(1); + } +} + +void CheckTest(bool act_val, bool exp_val, std::string desc) { + if (act_val && !exp_val) { + std::cerr << "error: " << desc << " was true, expected false" << std::endl; + exit(1); + } else if (!act_val && exp_val) { + std::cerr << "error: " << desc << " was false, expected true" << std::endl; + exit(1); + } +} + +void CheckReceive( + TestClient* client, bool should_receive, const char* buf, size_t size) { + if (should_receive) + client->CheckNextPacket(buf, size, 0); + else + client->CheckNoPacket(); +} + +TestClient* CreateTestClient( + SocketFactory* factory, const SocketAddress& local_addr) { + AsyncUDPSocket* socket = CreateAsyncUDPSocket(factory); + CHECK(socket->Bind(local_addr)); + return new TestClient(socket); +} + +void TestNATPorts( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4], + NATType nat_type, bool exp_same) { + + Thread th_int(internal); + Thread th_ext(external); + + SocketAddress server_addr = internal_addr; + server_addr.SetPort(NAT_SERVER_PORT); + NATServer* nat = new NATServer( + nat_type, internal, server_addr, external, external_addrs[0]); + NATSocketFactory* natsf = new NATSocketFactory(internal, server_addr); + + TestClient* in = CreateTestClient(natsf, internal_addr); + TestClient* out[4]; + for (int i = 0; i < 4; i++) + out[i] = CreateTestClient(external, external_addrs[i]); + + th_int.Start(); + th_ext.Start(); + + const char* buf = "filter_test"; + size_t len = strlen(buf); + + in->SendTo(buf, len, external_addrs[0]); + SocketAddress trans_addr; + out[0]->CheckNextPacket(buf, len, &trans_addr); + + for (int i = 1; i < 4; i++) { + in->SendTo(buf, len, external_addrs[i]); + SocketAddress trans_addr2; + out[i]->CheckNextPacket(buf, len, &trans_addr2); + bool are_same = (trans_addr == trans_addr2); + CheckTest(are_same, exp_same, "same translated address"); + } + + th_int.Stop(); + th_ext.Stop(); + + delete nat; + delete natsf; + delete in; + for (int i = 0; i < 4; i++) + delete out[i]; +} + +void TestPorts( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4]) { + TestNATPorts(internal, internal_addr, external, external_addrs, + NAT_OPEN_CONE, true); + TestNATPorts(internal, internal_addr, external, external_addrs, + NAT_ADDR_RESTRICTED, true); + TestNATPorts(internal, internal_addr, external, external_addrs, + NAT_PORT_RESTRICTED, true); + TestNATPorts(internal, internal_addr, external, external_addrs, + NAT_SYMMETRIC, false); +} + +void TestNATFilters( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4], + NATType nat_type, bool filter_ip, bool filter_port) { + + Thread th_int(internal); + Thread th_ext(external); + + SocketAddress server_addr = internal_addr; + server_addr.SetPort(NAT_SERVER_PORT); + NATServer* nat = new NATServer( + nat_type, internal, server_addr, external, external_addrs[0]); + NATSocketFactory* natsf = new NATSocketFactory(internal, server_addr); + + TestClient* in = CreateTestClient(natsf, internal_addr); + TestClient* out[4]; + for (int i = 0; i < 4; i++) + out[i] = CreateTestClient(external, external_addrs[i]); + + th_int.Start(); + th_ext.Start(); + + const char* buf = "filter_test"; + size_t len = strlen(buf); + + in->SendTo(buf, len, external_addrs[0]); + SocketAddress trans_addr; + out[0]->CheckNextPacket(buf, len, &trans_addr); + + out[1]->SendTo(buf, len, trans_addr); + CheckReceive(in, !filter_ip, buf, len); + + out[2]->SendTo(buf, len, trans_addr); + CheckReceive(in, !filter_port, buf, len); + + out[3]->SendTo(buf, len, trans_addr); + CheckReceive(in, !filter_ip && !filter_port, buf, len); + + th_int.Stop(); + th_ext.Stop(); + + delete nat; + delete natsf; + delete in; + for (int i = 0; i < 4; i++) + delete out[i]; +} + +void TestFilters( + SocketServer* internal, const SocketAddress& internal_addr, + SocketServer* external, const SocketAddress external_addrs[4]) { + TestNATFilters(internal, internal_addr, external, external_addrs, + NAT_OPEN_CONE, false, false); + TestNATFilters(internal, internal_addr, external, external_addrs, + NAT_ADDR_RESTRICTED, true, false); + TestNATFilters(internal, internal_addr, external, external_addrs, + NAT_PORT_RESTRICTED, true, true); + TestNATFilters(internal, internal_addr, external, external_addrs, + NAT_SYMMETRIC, true, true); +} + +const int PORT0 = 7405; +const int PORT1 = 7450; +const int PORT2 = 7505; + +int main(int argc, char* argv[]) { + assert(LocalHost().networks().size() >= 2); + SocketAddress int_addr(LocalHost().networks()[1]->ip(), PORT0); + + std::string ext_ip1 = + SocketAddress::IPToString(LocalHost().networks()[0]->ip()); // 127.0.0.1 + std::string ext_ip2 = + SocketAddress::IPToString(LocalHost().networks()[1]->ip()); // 127.0.0.2 + assert(int_addr.IPAsString() != ext_ip1); + //assert(int_addr.IPAsString() != ext_ip2); // uncomment + + SocketAddress ext_addrs[4] = { + SocketAddress(ext_ip1, PORT1), + SocketAddress(ext_ip2, PORT1), + SocketAddress(ext_ip1, PORT2), + SocketAddress(ext_ip2, PORT2) + }; + + PhysicalSocketServer* int_pss = new PhysicalSocketServer(); + PhysicalSocketServer* ext_pss = new PhysicalSocketServer(); + + std::cout << "Testing on physical network:" << std::endl; + TestPorts(int_pss, int_addr, ext_pss, ext_addrs); + std::cout << "ports: PASS" << std::endl; + TestFilters(int_pss, int_addr, ext_pss, ext_addrs); + std::cout << "filters: PASS" << std::endl; + + VirtualSocketServer* int_vss = new VirtualSocketServer(); + VirtualSocketServer* ext_vss = new VirtualSocketServer(); + + int_addr.SetIP(int_vss->GetNextIP()); + ext_addrs[0].SetIP(ext_vss->GetNextIP()); + ext_addrs[1].SetIP(ext_vss->GetNextIP()); + ext_addrs[2].SetIP(ext_addrs[0].ip()); + ext_addrs[3].SetIP(ext_addrs[1].ip()); + + std::cout << "Testing on virtual network:" << std::endl; + TestPorts(int_vss, int_addr, ext_vss, ext_addrs); + std::cout << "ports: PASS" << std::endl; + TestFilters(int_vss, int_addr, ext_vss, ext_addrs); + std::cout << "filters: PASS" << std::endl; + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/base/natserver.cc b/Plugins/jingle/libjingle/talk/base/natserver.cc new file mode 100644 index 0000000..336cf64 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/natserver.cc @@ -0,0 +1,210 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <cassert> +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +#include "talk/base/natserver.h" + +namespace talk_base { + +RouteCmp::RouteCmp(NAT* nat) : symmetric(nat->IsSymmetric()) { +} + +size_t RouteCmp::operator()(const SocketAddressPair& r) const { + size_t h = r.source().Hash(); + if (symmetric) + h ^= r.destination().Hash(); + return h; +} + +bool RouteCmp::operator()( + const SocketAddressPair& r1, const SocketAddressPair& r2) const { + if (r1.source() < r2.source()) + return true; + if (r2.source() < r1.source()) + return false; + if (symmetric && (r1.destination() < r2.destination())) + return true; + if (symmetric && (r2.destination() < r1.destination())) + return false; + return false; +} + +AddrCmp::AddrCmp(NAT* nat) + : use_ip(nat->FiltersIP()), use_port(nat->FiltersPort()) { +} + +size_t AddrCmp::operator()(const SocketAddress& a) const { + size_t h = 0; + if (use_ip) + h ^= a.ip(); + if (use_port) + h ^= a.port() | (a.port() << 16); + return h; +} + +bool AddrCmp::operator()( + const SocketAddress& a1, const SocketAddress& a2) const { + if (use_ip && (a1.ip() < a2.ip())) + return true; + if (use_ip && (a2.ip() < a1.ip())) + return false; + if (use_port && (a1.port() < a2.port())) + return true; + if (use_port && (a2.port() < a1.port())) + return false; + return false; +} + +NATServer::NATServer( + NATType type, SocketFactory* internal, const SocketAddress& internal_addr, + SocketFactory* external, const SocketAddress& external_ip) + : external_(external), external_ip_(external_ip) { + nat_ = NAT::Create(type); + + server_socket_ = CreateAsyncUDPSocket(internal); + server_socket_->Bind(internal_addr); + server_socket_->SignalReadPacket.connect(this, &NATServer::OnInternalPacket); + + int_map_ = new InternalMap(RouteCmp(nat_)); + ext_map_ = new ExternalMap(); +} + +NATServer::~NATServer() { + for (InternalMap::iterator iter = int_map_->begin(); + iter != int_map_->end(); + iter++) + delete iter->second; + + delete nat_; + delete server_socket_; + delete int_map_; + delete ext_map_; +} + +void NATServer::OnInternalPacket( + const char* buf, size_t size, const SocketAddress& addr, + AsyncPacketSocket* socket) { + + // Read the intended destination from the wire. + SocketAddress dest_addr; + dest_addr.Read_(buf, size); + + // Find the translation for these addresses (allocating one if necessary). + SocketAddressPair route(addr, dest_addr); + InternalMap::iterator iter = int_map_->find(route); + if (iter == int_map_->end()) { + Translate(route); + iter = int_map_->find(route); + } + assert(iter != int_map_->end()); + + // Allow the destination to send packets back to the source. + iter->second->whitelist->insert(dest_addr); + + // Send the packet to its intended destination. + iter->second->socket->SendTo( + buf + dest_addr.Size_(), size - dest_addr.Size_(), dest_addr); +} + +void NATServer::OnExternalPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + + SocketAddress local_addr = socket->GetLocalAddress(); + + // Find the translation for this addresses. + ExternalMap::iterator iter = ext_map_->find(local_addr); + assert(iter != ext_map_->end()); + + // Allow the NAT to reject this packet. + if (Filter(iter->second, remote_addr)) { + std::cerr << "Packet from " << remote_addr.ToString() + << " was filtered out by the NAT." << std::endl; + return; + } + + // Forward this packet to the internal address. + + size_t real_size = size + remote_addr.Size_(); + char* real_buf = new char[real_size]; + + remote_addr.Write_(real_buf, real_size); + std::memcpy(real_buf + remote_addr.Size_(), buf, size); + + server_socket_->SendTo(real_buf, real_size, iter->second->route.source()); + + delete[] real_buf; +} + +void NATServer::Translate(const SocketAddressPair& route) { + AsyncUDPSocket* socket = CreateAsyncUDPSocket(external_); + + SocketAddress ext_addr = external_ip_; + for (int i = 0; i < 65536; i++) { + ext_addr.SetPort((route.source().port() + i) % 65536); + if (ext_map_->find(ext_addr) == ext_map_->end()) { + int result = socket->Bind(ext_addr); + if ((result < 0) && (socket->GetError() == EADDRINUSE)) + continue; + assert(result >= 0); // TODO: do something better + + TransEntry* entry = new TransEntry(route, socket, nat_); + (*int_map_)[route] = entry; + (*ext_map_)[ext_addr] = entry; + socket->SignalReadPacket.connect(this, &NATServer::OnExternalPacket); + return; + } + } + + std::cerr << "Couldn't find a free port!" << std::endl; + delete socket; + exit(1); +} + +bool NATServer::Filter(TransEntry* entry, const SocketAddress& ext_addr) { + return entry->whitelist->find(ext_addr) == entry->whitelist->end(); +} + +NATServer::TransEntry::TransEntry( + const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat) + : route(r), socket(s) { + whitelist = new AddressSet(AddrCmp(nat)); +} + +NATServer::TransEntry::~TransEntry() { + delete socket; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/natserver.h b/Plugins/jingle/libjingle/talk/base/natserver.h new file mode 100644 index 0000000..61c1dd3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/natserver.h @@ -0,0 +1,114 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_NATSERVER_H__ +#define TALK_BASE_NATSERVER_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/socketaddresspair.h" +#include "talk/base/thread.h" +#include "talk/base/socketfactory.h" +#include "talk/base/nattypes.h" +#include <map> + +namespace talk_base { + +// Change how routes (socketaddress pairs) are compared based on the type of +// NAT. The NAT server maintains a hashtable of the routes that it knows +// about. So these affect which routes are treated the same. +struct RouteCmp { + RouteCmp(NAT* nat); + size_t operator()(const SocketAddressPair& r) const; + bool operator()( + const SocketAddressPair& r1, const SocketAddressPair& r2) const; + + bool symmetric; +}; + +// Changes how addresses are compared based on the filtering rules of the NAT. +struct AddrCmp { + AddrCmp(NAT* nat); + size_t operator()(const SocketAddress& r) const; + bool operator()(const SocketAddress& r1, const SocketAddress& r2) const; + + bool use_ip; + bool use_port; +}; + +// Implements the NAT device. It listens for packets on the internal network, +// translates them, and sends them out over the external network. + +const int NAT_SERVER_PORT = 4237; + +class NATServer : public sigslot::has_slots<> { +public: + NATServer( + NATType type, SocketFactory* internal, const SocketAddress& internal_addr, + SocketFactory* external, const SocketAddress& external_ip); + ~NATServer(); + + // Packets received on one of the networks. + void OnInternalPacket( + const char* buf, size_t size, const SocketAddress& addr, + AsyncPacketSocket* socket); + void OnExternalPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); + +private: + typedef std::set<SocketAddress,AddrCmp> AddressSet; + + /* Records a translation and the associated external socket. */ + struct TransEntry { + TransEntry(const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat); + ~TransEntry(); + + SocketAddressPair route; + AsyncUDPSocket* socket; + AddressSet* whitelist; + }; + + typedef std::map<SocketAddressPair,TransEntry*,RouteCmp> InternalMap; + typedef std::map<SocketAddress,TransEntry*> ExternalMap; + + NAT* nat_; + AsyncUDPSocket* server_socket_; + SocketFactory* external_; + SocketAddress external_ip_; + InternalMap* int_map_; + ExternalMap* ext_map_; + + /* Creates a new entry that translates the given route. */ + void Translate(const SocketAddressPair& route); + + /* Determines whether the NAT would filter out a packet from this address. */ + bool Filter(TransEntry* entry, const SocketAddress& ext_addr); +}; + +} // namespace talk_base + +#endif // TALK_BASE_NATSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/natserver_main.cc b/Plugins/jingle/libjingle/talk/base/natserver_main.cc new file mode 100644 index 0000000..a748108 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/natserver_main.cc @@ -0,0 +1,57 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <iostream> + +#include "talk/base/natserver.h" +#include "talk/base/host.h" +#include "talk/base/physicalsocketserver.h" + +using namespace talk_base; + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "usage: natserver <internal-ip> <external-ip>" << std::endl; + exit(1); + } + + SocketAddress internal = SocketAddress(argv[1]); + SocketAddress external = SocketAddress(argv[2]); + if (internal.EqualIPs(external)) { + std::cerr << "internal and external IPs must differ" << std::endl; + exit(1); + } + + Thread* pthMain = Thread::Current(); + PhysicalSocketServer* ss = new PhysicalSocketServer(); + pthMain->set_socketserver(ss); + NATServer* server = new NATServer(NAT_OPEN_CONE, ss, internal, ss, external); + server = server; + + pthMain->Run(); + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc b/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc new file mode 100644 index 0000000..2b0309e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/natsocketfactory.cc @@ -0,0 +1,228 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <iostream> +#include <cassert> +#include "talk/base/natsocketfactory.h" + +namespace talk_base { + +class NATSocket : public AsyncSocket { +public: + NATSocket(Socket* socket, const SocketAddress& server_addr) + : async_(false), connected_(false), server_addr_(server_addr), + socket_(socket), buf_(0), size_(0) { + } + + NATSocket(AsyncSocket* socket, const SocketAddress& server_addr) + : async_(true), connected_(false), server_addr_(server_addr), + socket_(socket), buf_(0), size_(0) { + socket->SignalReadEvent.connect(this, &NATSocket::OnReadEvent); + socket->SignalWriteEvent.connect(this, &NATSocket::OnWriteEvent); + } + + virtual ~NATSocket() { + delete socket_; + delete buf_; + } + + SocketAddress GetLocalAddress() const { + return socket_->GetLocalAddress(); + } + + SocketAddress GetRemoteAddress() const { + return remote_addr_; // will be ANY if not connected + } + + int Bind(const SocketAddress& addr) { + return socket_->Bind(addr); + } + + int Connect(const SocketAddress& addr) { + connected_ = true; + remote_addr_ = addr; + return 0; + } + + int Send(const void *pv, size_t cb) { + assert(connected_); + return SendInternal(pv, cb, remote_addr_); + } + + int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + assert(!connected_); + return SendInternal(pv, cb, addr); + } + + int SendInternal(const void *pv, size_t cb, const SocketAddress& addr) { + size_t size = cb + addr.Size_(); + char* buf = new char[size]; + Encode(static_cast<const char*>(pv), cb, buf, size, addr); + + int result = socket_->SendTo(buf, size, server_addr_); + delete buf; + if (result < 0) { + return result; + } else { + assert(result == static_cast<int>(size)); // TODO: This isn't fair. + return (int)((size_t)result - addr.Size_()); + } + } + + int Recv(void *pv, size_t cb) { + SocketAddress addr; + return RecvFrom(pv, cb, &addr); + } + + int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + // Make sure we have enough room to read the requested amount plus the + // header address. + SocketAddress remote_addr; + Grow(cb + remote_addr.Size_()); + + // Read the packet from the socket. + int result = socket_->RecvFrom(buf_, size_, &remote_addr); + if (result < 0) + return result; + assert(remote_addr == server_addr_); + + // TODO: we need better framing so that we know how many bytes we can + // return before we need to read the next address. For UDP, this will be + // fine as long as the reader always reads everything in the packet. + assert((size_t)result < size_); + + // Decode the wire packet into the actual results. + SocketAddress real_remote_addr; + size_t real_size = cb; + Decode(buf_, result, pv, &real_size, &real_remote_addr); + + // Make sure this packet should be delivered before returning it. + if (!connected_ || (real_remote_addr == remote_addr_)) { + if (paddr) + *paddr = real_remote_addr; + return (int)real_size; + } else { + std::cerr << "Dropping packet from unknown remote address: " + << real_remote_addr.ToString() << std::endl; + return 0; // Tell the caller we didn't read anything + } + } + + int Close() { + connected_ = false; + remote_addr_ = SocketAddress(); + return socket_->Close(); + } + + int Listen(int backlog) { + assert(false); // not yet implemented + return 0; + } + + Socket* Accept(SocketAddress *paddr) { + assert(false); // not yet implemented + return 0; + } + + AsyncSocket* asyncsocket() { + assert(async_); + return static_cast<AsyncSocket*>(socket_); + } + + int GetError() const { return socket_->GetError(); } + void SetError(int error) { socket_->SetError(error); } + + ConnState GetState() const { return connected_ ? CS_CONNECTED : CS_CLOSED; } + + virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); } + virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); } + + void OnReadEvent(AsyncSocket* socket) { + assert(socket == socket_); + SignalReadEvent(this); + } + + void OnWriteEvent(AsyncSocket* socket) { + assert(socket == socket_); + SignalWriteEvent(this); + } + +private: + // Makes sure the buffer is at least the given size. + void Grow(size_t new_size) { + if (size_ < new_size) { + delete buf_; + size_ = new_size; + buf_ = new char[size_]; + } + } + + // Encodes the given data and intended remote address into a packet to send + // to the NAT server. + void Encode(const char* data, size_t data_size, char* buf, size_t buf_size, + const SocketAddress& remote_addr) { + assert(buf_size == data_size + remote_addr.Size_()); + remote_addr.Write_(buf, (int)buf_size); + std::memcpy(buf + remote_addr.Size_(), data, data_size); + } + + // Decodes the given packet from the NAT server into the actual remote + // address and data. + void Decode(const char* data, size_t data_size, void* buf, size_t* buf_size, + SocketAddress* remote_addr) { + assert(data_size >= remote_addr->Size_()); + assert(data_size <= *buf_size + remote_addr->Size_()); + remote_addr->Read_(data, (int)data_size); + *buf_size = data_size - remote_addr->Size_(); + std::memcpy(buf, data + remote_addr->Size_(), *buf_size); + } + + bool async_; + bool connected_; + SocketAddress remote_addr_; + SocketAddress server_addr_; // address of the NAT server + Socket* socket_; + char* buf_; + size_t size_; +}; + +NATSocketFactory::NATSocketFactory( + SocketFactory* factory, const SocketAddress& nat_addr) + : factory_(factory), nat_addr_(nat_addr) { +} + +Socket* NATSocketFactory::CreateSocket(int type) { + assert(type == SOCK_DGRAM); // TCP is not yet suported + return new NATSocket(factory_->CreateSocket(type), nat_addr_); +} + +AsyncSocket* NATSocketFactory::CreateAsyncSocket(int type) { + assert(type == SOCK_DGRAM); // TCP is not yet suported + return new NATSocket(factory_->CreateAsyncSocket(type), nat_addr_); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/natsocketfactory.h b/Plugins/jingle/libjingle/talk/base/natsocketfactory.h new file mode 100644 index 0000000..a689158 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/natsocketfactory.h @@ -0,0 +1,51 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_NATSOCKETFACTORY_H__ +#define TALK_BASE_NATSOCKETFACTORY_H__ + +#include "talk/base/socketfactory.h" + +namespace talk_base { + +// Creates sockets that will send all traffic through a NAT. The actual data +// is sent using sockets from a socket factory, given to the constructor. +class NATSocketFactory : public SocketFactory { +public: + NATSocketFactory(SocketFactory* factory, const SocketAddress& nat_addr); + + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + +private: + SocketFactory* factory_; + SocketAddress nat_addr_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_NATSOCKETFACTORY_H__ diff --git a/Plugins/jingle/libjingle/talk/base/nattypes.cc b/Plugins/jingle/libjingle/talk/base/nattypes.cc new file mode 100644 index 0000000..290c3ad --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/nattypes.cc @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <cassert> + +#include "talk/base/nattypes.h" + +namespace talk_base { + +class SymmetricNAT : public NAT { +public: + bool IsSymmetric() { return true; } + bool FiltersIP() { return true; } + bool FiltersPort() { return true; } +}; + +class OpenConeNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return false; } + bool FiltersPort() { return false; } +}; + +class AddressRestrictedNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return true; } + bool FiltersPort() { return false; } +}; + +class PortRestrictedNAT : public NAT { +public: + bool IsSymmetric() { return false; } + bool FiltersIP() { return true; } + bool FiltersPort() { return true; } +}; + +NAT* NAT::Create(NATType type) { + switch (type) { + case NAT_OPEN_CONE: return new OpenConeNAT(); + case NAT_ADDR_RESTRICTED: return new AddressRestrictedNAT(); + case NAT_PORT_RESTRICTED: return new PortRestrictedNAT(); + case NAT_SYMMETRIC: return new SymmetricNAT(); + default: assert(0); return 0; + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/nattypes.h b/Plugins/jingle/libjingle/talk/base/nattypes.h new file mode 100644 index 0000000..aad11bb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/nattypes.h @@ -0,0 +1,62 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_NATTYPE_H__ +#define TALK_BASE_NATTYPE_H__ + +namespace talk_base { + +/* Identifies each type of NAT that can be simulated. */ +enum NATType { + NAT_OPEN_CONE, + NAT_ADDR_RESTRICTED, + NAT_PORT_RESTRICTED, + NAT_SYMMETRIC +}; + +// Implements the rules for each specific type of NAT. +class NAT { +public: + // Determines whether this NAT uses both source and destination address when + // checking whether a mapping already exists. + virtual bool IsSymmetric() = 0; + + // Determines whether this NAT drops packets received from a different IP + // the one last sent to. + virtual bool FiltersIP() = 0; + + // Determines whether this NAT drops packets received from a different port + // the one last sent to. + virtual bool FiltersPort() = 0; + + // Returns an implementation of the given type of NAT. + static NAT* Create(NATType type); +}; + +} // namespace talk_base + +#endif // TALK_BASE_NATTYPE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/netfw.h b/Plugins/jingle/libjingle/talk/base/netfw.h new file mode 100644 index 0000000..3a1e4a7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/netfw.h @@ -0,0 +1,3767 @@ +
+#pragma warning( disable: 4049 ) /* more than 64k source lines */
+
+/* this ALWAYS GENERATED file contains the definitions for the interfaces */
+
+
+ /* File created by MIDL compiler version 6.00.0347 */
+/* Compiler settings for netfw.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
+
+
+/* verify that the <rpcndr.h> version is high enough to compile this file*/
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+#include "rpc.h"
+#include "rpcndr.h"
+
+#ifndef __RPCNDR_H_VERSION__
+#error this stub requires an updated version of <rpcndr.h>
+#endif // __RPCNDR_H_VERSION__
+
+#ifndef COM_NO_WINDOWS_H
+#include "windows.h"
+#include "ole2.h"
+#endif /*COM_NO_WINDOWS_H*/
+
+#ifndef __netfw_h__
+#define __netfw_h__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+/* Forward Declarations */
+
+#ifndef __INetFwRemoteAdminSettings_FWD_DEFINED__
+#define __INetFwRemoteAdminSettings_FWD_DEFINED__
+typedef interface INetFwRemoteAdminSettings INetFwRemoteAdminSettings;
+#endif /* __INetFwRemoteAdminSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_FWD_DEFINED__
+#define __INetFwIcmpSettings_FWD_DEFINED__
+typedef interface INetFwIcmpSettings INetFwIcmpSettings;
+#endif /* __INetFwIcmpSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_FWD_DEFINED__
+#define __INetFwOpenPort_FWD_DEFINED__
+typedef interface INetFwOpenPort INetFwOpenPort;
+#endif /* __INetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_FWD_DEFINED__
+#define __INetFwOpenPorts_FWD_DEFINED__
+typedef interface INetFwOpenPorts INetFwOpenPorts;
+#endif /* __INetFwOpenPorts_FWD_DEFINED__ */
+
+
+#ifndef __INetFwService_FWD_DEFINED__
+#define __INetFwService_FWD_DEFINED__
+typedef interface INetFwService INetFwService;
+#endif /* __INetFwService_FWD_DEFINED__ */
+
+
+#ifndef __INetFwServices_FWD_DEFINED__
+#define __INetFwServices_FWD_DEFINED__
+typedef interface INetFwServices INetFwServices;
+#endif /* __INetFwServices_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_FWD_DEFINED__
+#define __INetFwAuthorizedApplication_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplication INetFwAuthorizedApplication;
+#endif /* __INetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_FWD_DEFINED__
+#define __INetFwAuthorizedApplications_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplications INetFwAuthorizedApplications;
+#endif /* __INetFwAuthorizedApplications_FWD_DEFINED__ */
+
+
+#ifndef __INetFwProfile_FWD_DEFINED__
+#define __INetFwProfile_FWD_DEFINED__
+typedef interface INetFwProfile INetFwProfile;
+#endif /* __INetFwProfile_FWD_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_FWD_DEFINED__
+#define __INetFwPolicy_FWD_DEFINED__
+typedef interface INetFwPolicy INetFwPolicy;
+#endif /* __INetFwPolicy_FWD_DEFINED__ */
+
+
+#ifndef __INetFwMgr_FWD_DEFINED__
+#define __INetFwMgr_FWD_DEFINED__
+typedef interface INetFwMgr INetFwMgr;
+#endif /* __INetFwMgr_FWD_DEFINED__ */
+
+
+#ifndef __INetFwRemoteAdminSettings_FWD_DEFINED__
+#define __INetFwRemoteAdminSettings_FWD_DEFINED__
+typedef interface INetFwRemoteAdminSettings INetFwRemoteAdminSettings;
+#endif /* __INetFwRemoteAdminSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_FWD_DEFINED__
+#define __INetFwIcmpSettings_FWD_DEFINED__
+typedef interface INetFwIcmpSettings INetFwIcmpSettings;
+#endif /* __INetFwIcmpSettings_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_FWD_DEFINED__
+#define __INetFwOpenPort_FWD_DEFINED__
+typedef interface INetFwOpenPort INetFwOpenPort;
+#endif /* __INetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_FWD_DEFINED__
+#define __INetFwOpenPorts_FWD_DEFINED__
+typedef interface INetFwOpenPorts INetFwOpenPorts;
+#endif /* __INetFwOpenPorts_FWD_DEFINED__ */
+
+
+#ifndef __INetFwService_FWD_DEFINED__
+#define __INetFwService_FWD_DEFINED__
+typedef interface INetFwService INetFwService;
+#endif /* __INetFwService_FWD_DEFINED__ */
+
+
+#ifndef __INetFwServices_FWD_DEFINED__
+#define __INetFwServices_FWD_DEFINED__
+typedef interface INetFwServices INetFwServices;
+#endif /* __INetFwServices_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_FWD_DEFINED__
+#define __INetFwAuthorizedApplication_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplication INetFwAuthorizedApplication;
+#endif /* __INetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_FWD_DEFINED__
+#define __INetFwAuthorizedApplications_FWD_DEFINED__
+typedef interface INetFwAuthorizedApplications INetFwAuthorizedApplications;
+#endif /* __INetFwAuthorizedApplications_FWD_DEFINED__ */
+
+
+#ifndef __INetFwProfile_FWD_DEFINED__
+#define __INetFwProfile_FWD_DEFINED__
+typedef interface INetFwProfile INetFwProfile;
+#endif /* __INetFwProfile_FWD_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_FWD_DEFINED__
+#define __INetFwPolicy_FWD_DEFINED__
+typedef interface INetFwPolicy INetFwPolicy;
+#endif /* __INetFwPolicy_FWD_DEFINED__ */
+
+
+#ifndef __INetFwMgr_FWD_DEFINED__
+#define __INetFwMgr_FWD_DEFINED__
+typedef interface INetFwMgr INetFwMgr;
+#endif /* __INetFwMgr_FWD_DEFINED__ */
+
+
+#ifndef __NetFwOpenPort_FWD_DEFINED__
+#define __NetFwOpenPort_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwOpenPort NetFwOpenPort;
+#else
+typedef struct NetFwOpenPort NetFwOpenPort;
+#endif /* __cplusplus */
+
+#endif /* __NetFwOpenPort_FWD_DEFINED__ */
+
+
+#ifndef __NetFwAuthorizedApplication_FWD_DEFINED__
+#define __NetFwAuthorizedApplication_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwAuthorizedApplication NetFwAuthorizedApplication;
+#else
+typedef struct NetFwAuthorizedApplication NetFwAuthorizedApplication;
+#endif /* __cplusplus */
+
+#endif /* __NetFwAuthorizedApplication_FWD_DEFINED__ */
+
+
+#ifndef __NetFwMgr_FWD_DEFINED__
+#define __NetFwMgr_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class NetFwMgr NetFwMgr;
+#else
+typedef struct NetFwMgr NetFwMgr;
+#endif /* __cplusplus */
+
+#endif /* __NetFwMgr_FWD_DEFINED__ */
+
+
+/* header files for imported files */
+#include "icftypes.h"
+#include "oaidl.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void * __RPC_USER MIDL_user_allocate(size_t);
+void __RPC_USER MIDL_user_free( void * );
+
+#ifndef __INetFwRemoteAdminSettings_INTERFACE_DEFINED__
+#define __INetFwRemoteAdminSettings_INTERFACE_DEFINED__
+
+/* interface INetFwRemoteAdminSettings */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwRemoteAdminSettings;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("D4BECDDF-6F73-4A83-B832-9C66874CD20E")
+ INetFwRemoteAdminSettings : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwRemoteAdminSettingsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwRemoteAdminSettings * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwRemoteAdminSettings * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwRemoteAdminSettings * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ END_INTERFACE
+ } INetFwRemoteAdminSettingsVtbl;
+
+ interface INetFwRemoteAdminSettings
+ {
+ CONST_VTBL struct INetFwRemoteAdminSettingsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwRemoteAdminSettings_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwRemoteAdminSettings_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwRemoteAdminSettings_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwRemoteAdminSettings_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwRemoteAdminSettings_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwRemoteAdminSettings_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwRemoteAdminSettings_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwRemoteAdminSettings_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwRemoteAdminSettings_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwRemoteAdminSettings_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwRemoteAdminSettings_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwRemoteAdminSettings_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwRemoteAdminSettings_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwRemoteAdminSettings_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwRemoteAdminSettings_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_IpVersion_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_IpVersion_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_Scope_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_Scope_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_RemoteAddresses_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_RemoteAddresses_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_get_Enabled_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwRemoteAdminSettings_put_Enabled_Proxy(
+ INetFwRemoteAdminSettings * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwRemoteAdminSettings_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwRemoteAdminSettings_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwIcmpSettings_INTERFACE_DEFINED__
+#define __INetFwIcmpSettings_INTERFACE_DEFINED__
+
+/* interface INetFwIcmpSettings */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwIcmpSettings;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("A6207B2E-7CDD-426A-951E-5E1CBC5AFEAD")
+ INetFwIcmpSettings : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundDestinationUnreachable(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundDestinationUnreachable(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowRedirect(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowRedirect(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundEchoRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundEchoRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundTimeExceeded(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundTimeExceeded(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundParameterProblem(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundParameterProblem(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundSourceQuench(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundSourceQuench(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundRouterRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundRouterRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundTimestampRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundTimestampRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowInboundMaskRequest(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowInboundMaskRequest(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AllowOutboundPacketTooBig(
+ /* [retval][out] */ VARIANT_BOOL *allow) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_AllowOutboundPacketTooBig(
+ /* [in] */ VARIANT_BOOL allow) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwIcmpSettingsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwIcmpSettings * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwIcmpSettings * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwIcmpSettings * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwIcmpSettings * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwIcmpSettings * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwIcmpSettings * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwIcmpSettings * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundDestinationUnreachable )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundDestinationUnreachable )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowRedirect )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowRedirect )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundEchoRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundEchoRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundTimeExceeded )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundTimeExceeded )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundParameterProblem )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundParameterProblem )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundSourceQuench )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundSourceQuench )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundRouterRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundRouterRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundTimestampRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundTimestampRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowInboundMaskRequest )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowInboundMaskRequest )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AllowOutboundPacketTooBig )(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_AllowOutboundPacketTooBig )(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+ END_INTERFACE
+ } INetFwIcmpSettingsVtbl;
+
+ interface INetFwIcmpSettings
+ {
+ CONST_VTBL struct INetFwIcmpSettingsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwIcmpSettings_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwIcmpSettings_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwIcmpSettings_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwIcmpSettings_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwIcmpSettings_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwIcmpSettings_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwIcmpSettings_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundDestinationUnreachable(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundDestinationUnreachable(This,allow)
+
+#define INetFwIcmpSettings_get_AllowRedirect(This,allow) \
+ (This)->lpVtbl -> get_AllowRedirect(This,allow)
+
+#define INetFwIcmpSettings_put_AllowRedirect(This,allow) \
+ (This)->lpVtbl -> put_AllowRedirect(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundEchoRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundEchoRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundEchoRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundEchoRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundTimeExceeded(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundTimeExceeded(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundTimeExceeded(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundTimeExceeded(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundParameterProblem(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundParameterProblem(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundParameterProblem(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundParameterProblem(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundSourceQuench(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundSourceQuench(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundSourceQuench(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundSourceQuench(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundRouterRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundRouterRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundRouterRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundRouterRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundTimestampRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundTimestampRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundTimestampRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundTimestampRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowInboundMaskRequest(This,allow) \
+ (This)->lpVtbl -> get_AllowInboundMaskRequest(This,allow)
+
+#define INetFwIcmpSettings_put_AllowInboundMaskRequest(This,allow) \
+ (This)->lpVtbl -> put_AllowInboundMaskRequest(This,allow)
+
+#define INetFwIcmpSettings_get_AllowOutboundPacketTooBig(This,allow) \
+ (This)->lpVtbl -> get_AllowOutboundPacketTooBig(This,allow)
+
+#define INetFwIcmpSettings_put_AllowOutboundPacketTooBig(This,allow) \
+ (This)->lpVtbl -> put_AllowOutboundPacketTooBig(This,allow)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundDestinationUnreachable_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundDestinationUnreachable_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowRedirect_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowRedirect_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowRedirect_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowRedirect_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundEchoRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundEchoRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundEchoRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundEchoRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundTimeExceeded_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundTimeExceeded_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundTimeExceeded_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundTimeExceeded_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundParameterProblem_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundParameterProblem_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundParameterProblem_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundParameterProblem_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundSourceQuench_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundSourceQuench_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundSourceQuench_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundSourceQuench_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundRouterRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundRouterRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundRouterRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundRouterRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundTimestampRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundTimestampRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundTimestampRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundTimestampRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowInboundMaskRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowInboundMaskRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowInboundMaskRequest_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowInboundMaskRequest_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_get_AllowOutboundPacketTooBig_Proxy(
+ INetFwIcmpSettings * This,
+ /* [retval][out] */ VARIANT_BOOL *allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_get_AllowOutboundPacketTooBig_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwIcmpSettings_put_AllowOutboundPacketTooBig_Proxy(
+ INetFwIcmpSettings * This,
+ /* [in] */ VARIANT_BOOL allow);
+
+
+void __RPC_STUB INetFwIcmpSettings_put_AllowOutboundPacketTooBig_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwIcmpSettings_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwOpenPort_INTERFACE_DEFINED__
+#define __INetFwOpenPort_INTERFACE_DEFINED__
+
+/* interface INetFwOpenPort */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwOpenPort;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("E0483BA0-47FF-4D9C-A6D6-7741D0B195F7")
+ INetFwOpenPort : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Name(
+ /* [in] */ BSTR name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Protocol(
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Protocol(
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Port(
+ /* [retval][out] */ LONG *portNumber) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Port(
+ /* [in] */ LONG portNumber) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_BuiltIn(
+ /* [retval][out] */ VARIANT_BOOL *builtIn) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwOpenPortVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwOpenPort * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwOpenPort * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwOpenPort * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwOpenPort * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwOpenPort * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwOpenPort * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwOpenPort * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Name )(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Protocol )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Protocol )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Port )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ LONG *portNumber);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Port )(
+ INetFwOpenPort * This,
+ /* [in] */ LONG portNumber);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwOpenPort * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_BuiltIn )(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *builtIn);
+
+ END_INTERFACE
+ } INetFwOpenPortVtbl;
+
+ interface INetFwOpenPort
+ {
+ CONST_VTBL struct INetFwOpenPortVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwOpenPort_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwOpenPort_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwOpenPort_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwOpenPort_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwOpenPort_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwOpenPort_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwOpenPort_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwOpenPort_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwOpenPort_put_Name(This,name) \
+ (This)->lpVtbl -> put_Name(This,name)
+
+#define INetFwOpenPort_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwOpenPort_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwOpenPort_get_Protocol(This,ipProtocol) \
+ (This)->lpVtbl -> get_Protocol(This,ipProtocol)
+
+#define INetFwOpenPort_put_Protocol(This,ipProtocol) \
+ (This)->lpVtbl -> put_Protocol(This,ipProtocol)
+
+#define INetFwOpenPort_get_Port(This,portNumber) \
+ (This)->lpVtbl -> get_Port(This,portNumber)
+
+#define INetFwOpenPort_put_Port(This,portNumber) \
+ (This)->lpVtbl -> put_Port(This,portNumber)
+
+#define INetFwOpenPort_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwOpenPort_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwOpenPort_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwOpenPort_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwOpenPort_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwOpenPort_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#define INetFwOpenPort_get_BuiltIn(This,builtIn) \
+ (This)->lpVtbl -> get_BuiltIn(This,builtIn)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Name_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwOpenPort_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Name_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR name);
+
+
+void __RPC_STUB INetFwOpenPort_put_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_IpVersion_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwOpenPort_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_IpVersion_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwOpenPort_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Protocol_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_IP_PROTOCOL *ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPort_get_Protocol_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Protocol_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPort_put_Protocol_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Port_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ LONG *portNumber);
+
+
+void __RPC_STUB INetFwOpenPort_get_Port_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Port_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ LONG portNumber);
+
+
+void __RPC_STUB INetFwOpenPort_put_Port_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Scope_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwOpenPort_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Scope_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwOpenPort_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_RemoteAddresses_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwOpenPort_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_RemoteAddresses_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwOpenPort_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_Enabled_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwOpenPort_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_put_Enabled_Proxy(
+ INetFwOpenPort * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwOpenPort_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPort_get_BuiltIn_Proxy(
+ INetFwOpenPort * This,
+ /* [retval][out] */ VARIANT_BOOL *builtIn);
+
+
+void __RPC_STUB INetFwOpenPort_get_BuiltIn_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwOpenPort_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwOpenPorts_INTERFACE_DEFINED__
+#define __INetFwOpenPorts_INTERFACE_DEFINED__
+
+/* interface INetFwOpenPorts */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwOpenPorts;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("C0E9D7FA-E07E-430A-B19A-090CE82D92E2")
+ INetFwOpenPorts : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Add(
+ /* [in] */ INetFwOpenPort *port) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Remove(
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwOpenPortsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwOpenPorts * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwOpenPorts * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwOpenPorts * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwOpenPorts * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwOpenPorts * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwOpenPorts * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwOpenPorts * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Add )(
+ INetFwOpenPorts * This,
+ /* [in] */ INetFwOpenPort *port);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Remove )(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwOpenPortsVtbl;
+
+ interface INetFwOpenPorts
+ {
+ CONST_VTBL struct INetFwOpenPortsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwOpenPorts_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwOpenPorts_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwOpenPorts_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwOpenPorts_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwOpenPorts_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwOpenPorts_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwOpenPorts_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwOpenPorts_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwOpenPorts_Add(This,port) \
+ (This)->lpVtbl -> Add(This,port)
+
+#define INetFwOpenPorts_Remove(This,portNumber,ipProtocol) \
+ (This)->lpVtbl -> Remove(This,portNumber,ipProtocol)
+
+#define INetFwOpenPorts_Item(This,portNumber,ipProtocol,openPort) \
+ (This)->lpVtbl -> Item(This,portNumber,ipProtocol,openPort)
+
+#define INetFwOpenPorts_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_get_Count_Proxy(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwOpenPorts_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Add_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ INetFwOpenPort *port);
+
+
+void __RPC_STUB INetFwOpenPorts_Add_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Remove_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol);
+
+
+void __RPC_STUB INetFwOpenPorts_Remove_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_Item_Proxy(
+ INetFwOpenPorts * This,
+ /* [in] */ LONG portNumber,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [retval][out] */ INetFwOpenPort **openPort);
+
+
+void __RPC_STUB INetFwOpenPorts_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwOpenPorts_get__NewEnum_Proxy(
+ INetFwOpenPorts * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwOpenPorts_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwOpenPorts_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwService_INTERFACE_DEFINED__
+#define __INetFwService_INTERFACE_DEFINED__
+
+/* interface INetFwService */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwService;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("79FD57C8-908E-4A36-9888-D5B3F0A444CF")
+ INetFwService : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Type(
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Customized(
+ /* [retval][out] */ VARIANT_BOOL *customized) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_GloballyOpenPorts(
+ /* [retval][out] */ INetFwOpenPorts **openPorts) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwServiceVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwService * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwService * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwService * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwService * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwService * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwService * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwService * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Type )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Customized )(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *customized);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwService * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwService * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwService * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwService * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_GloballyOpenPorts )(
+ INetFwService * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+ END_INTERFACE
+ } INetFwServiceVtbl;
+
+ interface INetFwService
+ {
+ CONST_VTBL struct INetFwServiceVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwService_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwService_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwService_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwService_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwService_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwService_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwService_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwService_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwService_get_Type(This,type) \
+ (This)->lpVtbl -> get_Type(This,type)
+
+#define INetFwService_get_Customized(This,customized) \
+ (This)->lpVtbl -> get_Customized(This,customized)
+
+#define INetFwService_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwService_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwService_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwService_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwService_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwService_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwService_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwService_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#define INetFwService_get_GloballyOpenPorts(This,openPorts) \
+ (This)->lpVtbl -> get_GloballyOpenPorts(This,openPorts)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Name_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwService_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Type_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SERVICE_TYPE *type);
+
+
+void __RPC_STUB INetFwService_get_Type_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Customized_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *customized);
+
+
+void __RPC_STUB INetFwService_get_Customized_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_IpVersion_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwService_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_IpVersion_Proxy(
+ INetFwService * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwService_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Scope_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwService_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_Scope_Proxy(
+ INetFwService * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwService_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_RemoteAddresses_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwService_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_RemoteAddresses_Proxy(
+ INetFwService * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwService_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_Enabled_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwService_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwService_put_Enabled_Proxy(
+ INetFwService * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwService_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwService_get_GloballyOpenPorts_Proxy(
+ INetFwService * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+
+void __RPC_STUB INetFwService_get_GloballyOpenPorts_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwService_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwServices_INTERFACE_DEFINED__
+#define __INetFwServices_INTERFACE_DEFINED__
+
+/* interface INetFwServices */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwServices;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("79649BB4-903E-421B-94C9-79848E79F6EE")
+ INetFwServices : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwServicesVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwServices * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwServices * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwServices * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwServices * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwServices * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwServices * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwServices * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwServices * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwServices * This,
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwServices * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwServicesVtbl;
+
+ interface INetFwServices
+ {
+ CONST_VTBL struct INetFwServicesVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwServices_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwServices_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwServices_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwServices_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwServices_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwServices_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwServices_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwServices_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwServices_Item(This,svcType,service) \
+ (This)->lpVtbl -> Item(This,svcType,service)
+
+#define INetFwServices_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwServices_get_Count_Proxy(
+ INetFwServices * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwServices_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwServices_Item_Proxy(
+ INetFwServices * This,
+ /* [in] */ NET_FW_SERVICE_TYPE svcType,
+ /* [retval][out] */ INetFwService **service);
+
+
+void __RPC_STUB INetFwServices_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwServices_get__NewEnum_Proxy(
+ INetFwServices * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwServices_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwServices_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplication_INTERFACE_DEFINED__
+#define __INetFwAuthorizedApplication_INTERFACE_DEFINED__
+
+/* interface INetFwAuthorizedApplication */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwAuthorizedApplication;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("B5E64FFA-C2C5-444E-A301-FB5E00018050")
+ INetFwAuthorizedApplication : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Name(
+ /* [retval][out] */ BSTR *name) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Name(
+ /* [in] */ BSTR name) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ProcessImageFileName(
+ /* [retval][out] */ BSTR *imageFileName) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ProcessImageFileName(
+ /* [in] */ BSTR imageFileName) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IpVersion(
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_IpVersion(
+ /* [in] */ NET_FW_IP_VERSION ipVersion) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Scope(
+ /* [retval][out] */ NET_FW_SCOPE *scope) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Scope(
+ /* [in] */ NET_FW_SCOPE scope) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAddresses(
+ /* [retval][out] */ BSTR *remoteAddrs) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_RemoteAddresses(
+ /* [in] */ BSTR remoteAddrs) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Enabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_Enabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwAuthorizedApplicationVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwAuthorizedApplication * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwAuthorizedApplication * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwAuthorizedApplication * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Name )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *name);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Name )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR name);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_ProcessImageFileName )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *imageFileName);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_ProcessImageFileName )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR imageFileName);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IpVersion )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_IpVersion )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Scope )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Scope )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAddresses )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_RemoteAddresses )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR remoteAddrs);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Enabled )(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_Enabled )(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ END_INTERFACE
+ } INetFwAuthorizedApplicationVtbl;
+
+ interface INetFwAuthorizedApplication
+ {
+ CONST_VTBL struct INetFwAuthorizedApplicationVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwAuthorizedApplication_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwAuthorizedApplication_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwAuthorizedApplication_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwAuthorizedApplication_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwAuthorizedApplication_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwAuthorizedApplication_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwAuthorizedApplication_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwAuthorizedApplication_get_Name(This,name) \
+ (This)->lpVtbl -> get_Name(This,name)
+
+#define INetFwAuthorizedApplication_put_Name(This,name) \
+ (This)->lpVtbl -> put_Name(This,name)
+
+#define INetFwAuthorizedApplication_get_ProcessImageFileName(This,imageFileName) \
+ (This)->lpVtbl -> get_ProcessImageFileName(This,imageFileName)
+
+#define INetFwAuthorizedApplication_put_ProcessImageFileName(This,imageFileName) \
+ (This)->lpVtbl -> put_ProcessImageFileName(This,imageFileName)
+
+#define INetFwAuthorizedApplication_get_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> get_IpVersion(This,ipVersion)
+
+#define INetFwAuthorizedApplication_put_IpVersion(This,ipVersion) \
+ (This)->lpVtbl -> put_IpVersion(This,ipVersion)
+
+#define INetFwAuthorizedApplication_get_Scope(This,scope) \
+ (This)->lpVtbl -> get_Scope(This,scope)
+
+#define INetFwAuthorizedApplication_put_Scope(This,scope) \
+ (This)->lpVtbl -> put_Scope(This,scope)
+
+#define INetFwAuthorizedApplication_get_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> get_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwAuthorizedApplication_put_RemoteAddresses(This,remoteAddrs) \
+ (This)->lpVtbl -> put_RemoteAddresses(This,remoteAddrs)
+
+#define INetFwAuthorizedApplication_get_Enabled(This,enabled) \
+ (This)->lpVtbl -> get_Enabled(This,enabled)
+
+#define INetFwAuthorizedApplication_put_Enabled(This,enabled) \
+ (This)->lpVtbl -> put_Enabled(This,enabled)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Name_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *name);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Name_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR name);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Name_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_ProcessImageFileName_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_ProcessImageFileName_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_ProcessImageFileName_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_ProcessImageFileName_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_IpVersion_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_IP_VERSION *ipVersion);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_IpVersion_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_IpVersion_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Scope_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ NET_FW_SCOPE *scope);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Scope_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ NET_FW_SCOPE scope);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Scope_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_RemoteAddresses_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ BSTR *remoteAddrs);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_RemoteAddresses_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ BSTR remoteAddrs);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_RemoteAddresses_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_get_Enabled_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_get_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplication_put_Enabled_Proxy(
+ INetFwAuthorizedApplication * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwAuthorizedApplication_put_Enabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwAuthorizedApplication_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwAuthorizedApplications_INTERFACE_DEFINED__
+#define __INetFwAuthorizedApplications_INTERFACE_DEFINED__
+
+/* interface INetFwAuthorizedApplications */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwAuthorizedApplications;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("644EFD52-CCF9-486C-97A2-39F352570B30")
+ INetFwAuthorizedApplications : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Count(
+ /* [retval][out] */ long *count) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Add(
+ /* [in] */ INetFwAuthorizedApplication *app) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Remove(
+ /* [in] */ BSTR imageFileName) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE Item(
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app) = 0;
+
+ virtual /* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE get__NewEnum(
+ /* [retval][out] */ IUnknown **newEnum) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwAuthorizedApplicationsVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwAuthorizedApplications * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwAuthorizedApplications * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwAuthorizedApplications * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Count )(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ long *count);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Add )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ INetFwAuthorizedApplication *app);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Remove )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *Item )(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app);
+
+ /* [restricted][propget][id] */ HRESULT ( STDMETHODCALLTYPE *get__NewEnum )(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+ END_INTERFACE
+ } INetFwAuthorizedApplicationsVtbl;
+
+ interface INetFwAuthorizedApplications
+ {
+ CONST_VTBL struct INetFwAuthorizedApplicationsVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwAuthorizedApplications_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwAuthorizedApplications_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwAuthorizedApplications_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwAuthorizedApplications_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwAuthorizedApplications_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwAuthorizedApplications_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwAuthorizedApplications_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwAuthorizedApplications_get_Count(This,count) \
+ (This)->lpVtbl -> get_Count(This,count)
+
+#define INetFwAuthorizedApplications_Add(This,app) \
+ (This)->lpVtbl -> Add(This,app)
+
+#define INetFwAuthorizedApplications_Remove(This,imageFileName) \
+ (This)->lpVtbl -> Remove(This,imageFileName)
+
+#define INetFwAuthorizedApplications_Item(This,imageFileName,app) \
+ (This)->lpVtbl -> Item(This,imageFileName,app)
+
+#define INetFwAuthorizedApplications_get__NewEnum(This,newEnum) \
+ (This)->lpVtbl -> get__NewEnum(This,newEnum)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_get_Count_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ long *count);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_get_Count_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Add_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ INetFwAuthorizedApplication *app);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Add_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Remove_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Remove_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_Item_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [in] */ BSTR imageFileName,
+ /* [retval][out] */ INetFwAuthorizedApplication **app);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_Item_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [restricted][propget][id] */ HRESULT STDMETHODCALLTYPE INetFwAuthorizedApplications_get__NewEnum_Proxy(
+ INetFwAuthorizedApplications * This,
+ /* [retval][out] */ IUnknown **newEnum);
+
+
+void __RPC_STUB INetFwAuthorizedApplications_get__NewEnum_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwAuthorizedApplications_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwProfile_INTERFACE_DEFINED__
+#define __INetFwProfile_INTERFACE_DEFINED__
+
+/* interface INetFwProfile */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwProfile;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("174A0DDA-E9F9-449D-993B-21AB667CA456")
+ INetFwProfile : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Type(
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_FirewallEnabled(
+ /* [retval][out] */ VARIANT_BOOL *enabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_FirewallEnabled(
+ /* [in] */ VARIANT_BOOL enabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ExceptionsNotAllowed(
+ /* [retval][out] */ VARIANT_BOOL *notAllowed) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ExceptionsNotAllowed(
+ /* [in] */ VARIANT_BOOL notAllowed) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_NotificationsDisabled(
+ /* [retval][out] */ VARIANT_BOOL *disabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_NotificationsDisabled(
+ /* [in] */ VARIANT_BOOL disabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_UnicastResponsesToMulticastBroadcastDisabled(
+ /* [retval][out] */ VARIANT_BOOL *disabled) = 0;
+
+ virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_UnicastResponsesToMulticastBroadcastDisabled(
+ /* [in] */ VARIANT_BOOL disabled) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_RemoteAdminSettings(
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_IcmpSettings(
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_GloballyOpenPorts(
+ /* [retval][out] */ INetFwOpenPorts **openPorts) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_Services(
+ /* [retval][out] */ INetFwServices **services) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_AuthorizedApplications(
+ /* [retval][out] */ INetFwAuthorizedApplications **apps) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwProfileVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwProfile * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwProfile * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwProfile * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwProfile * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwProfile * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwProfile * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwProfile * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Type )(
+ INetFwProfile * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_FirewallEnabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_FirewallEnabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_ExceptionsNotAllowed )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *notAllowed);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_ExceptionsNotAllowed )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL notAllowed);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_NotificationsDisabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_NotificationsDisabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_UnicastResponsesToMulticastBroadcastDisabled )(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+ /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_UnicastResponsesToMulticastBroadcastDisabled )(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_RemoteAdminSettings )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_IcmpSettings )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_GloballyOpenPorts )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_Services )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwServices **services);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_AuthorizedApplications )(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwAuthorizedApplications **apps);
+
+ END_INTERFACE
+ } INetFwProfileVtbl;
+
+ interface INetFwProfile
+ {
+ CONST_VTBL struct INetFwProfileVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwProfile_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwProfile_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwProfile_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwProfile_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwProfile_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwProfile_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwProfile_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwProfile_get_Type(This,type) \
+ (This)->lpVtbl -> get_Type(This,type)
+
+#define INetFwProfile_get_FirewallEnabled(This,enabled) \
+ (This)->lpVtbl -> get_FirewallEnabled(This,enabled)
+
+#define INetFwProfile_put_FirewallEnabled(This,enabled) \
+ (This)->lpVtbl -> put_FirewallEnabled(This,enabled)
+
+#define INetFwProfile_get_ExceptionsNotAllowed(This,notAllowed) \
+ (This)->lpVtbl -> get_ExceptionsNotAllowed(This,notAllowed)
+
+#define INetFwProfile_put_ExceptionsNotAllowed(This,notAllowed) \
+ (This)->lpVtbl -> put_ExceptionsNotAllowed(This,notAllowed)
+
+#define INetFwProfile_get_NotificationsDisabled(This,disabled) \
+ (This)->lpVtbl -> get_NotificationsDisabled(This,disabled)
+
+#define INetFwProfile_put_NotificationsDisabled(This,disabled) \
+ (This)->lpVtbl -> put_NotificationsDisabled(This,disabled)
+
+#define INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled(This,disabled) \
+ (This)->lpVtbl -> get_UnicastResponsesToMulticastBroadcastDisabled(This,disabled)
+
+#define INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled(This,disabled) \
+ (This)->lpVtbl -> put_UnicastResponsesToMulticastBroadcastDisabled(This,disabled)
+
+#define INetFwProfile_get_RemoteAdminSettings(This,remoteAdminSettings) \
+ (This)->lpVtbl -> get_RemoteAdminSettings(This,remoteAdminSettings)
+
+#define INetFwProfile_get_IcmpSettings(This,icmpSettings) \
+ (This)->lpVtbl -> get_IcmpSettings(This,icmpSettings)
+
+#define INetFwProfile_get_GloballyOpenPorts(This,openPorts) \
+ (This)->lpVtbl -> get_GloballyOpenPorts(This,openPorts)
+
+#define INetFwProfile_get_Services(This,services) \
+ (This)->lpVtbl -> get_Services(This,services)
+
+#define INetFwProfile_get_AuthorizedApplications(This,apps) \
+ (This)->lpVtbl -> get_AuthorizedApplications(This,apps)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_Type_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *type);
+
+
+void __RPC_STUB INetFwProfile_get_Type_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_FirewallEnabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *enabled);
+
+
+void __RPC_STUB INetFwProfile_get_FirewallEnabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_FirewallEnabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL enabled);
+
+
+void __RPC_STUB INetFwProfile_put_FirewallEnabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_ExceptionsNotAllowed_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *notAllowed);
+
+
+void __RPC_STUB INetFwProfile_get_ExceptionsNotAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_ExceptionsNotAllowed_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL notAllowed);
+
+
+void __RPC_STUB INetFwProfile_put_ExceptionsNotAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_NotificationsDisabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+
+void __RPC_STUB INetFwProfile_get_NotificationsDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_NotificationsDisabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+
+void __RPC_STUB INetFwProfile_put_NotificationsDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ VARIANT_BOOL *disabled);
+
+
+void __RPC_STUB INetFwProfile_get_UnicastResponsesToMulticastBroadcastDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propput][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled_Proxy(
+ INetFwProfile * This,
+ /* [in] */ VARIANT_BOOL disabled);
+
+
+void __RPC_STUB INetFwProfile_put_UnicastResponsesToMulticastBroadcastDisabled_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_RemoteAdminSettings_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwRemoteAdminSettings **remoteAdminSettings);
+
+
+void __RPC_STUB INetFwProfile_get_RemoteAdminSettings_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_IcmpSettings_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwIcmpSettings **icmpSettings);
+
+
+void __RPC_STUB INetFwProfile_get_IcmpSettings_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_GloballyOpenPorts_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwOpenPorts **openPorts);
+
+
+void __RPC_STUB INetFwProfile_get_GloballyOpenPorts_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_Services_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwServices **services);
+
+
+void __RPC_STUB INetFwProfile_get_Services_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwProfile_get_AuthorizedApplications_Proxy(
+ INetFwProfile * This,
+ /* [retval][out] */ INetFwAuthorizedApplications **apps);
+
+
+void __RPC_STUB INetFwProfile_get_AuthorizedApplications_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwProfile_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwPolicy_INTERFACE_DEFINED__
+#define __INetFwPolicy_INTERFACE_DEFINED__
+
+/* interface INetFwPolicy */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwPolicy;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("D46D2478-9AC9-4008-9DC7-5563CE5536CC")
+ INetFwPolicy : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CurrentProfile(
+ /* [retval][out] */ INetFwProfile **profile) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE GetProfileByType(
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwPolicyVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwPolicy * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwPolicy * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwPolicy * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwPolicy * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwPolicy * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwPolicy * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwPolicy * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_CurrentProfile )(
+ INetFwPolicy * This,
+ /* [retval][out] */ INetFwProfile **profile);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *GetProfileByType )(
+ INetFwPolicy * This,
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile);
+
+ END_INTERFACE
+ } INetFwPolicyVtbl;
+
+ interface INetFwPolicy
+ {
+ CONST_VTBL struct INetFwPolicyVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwPolicy_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwPolicy_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwPolicy_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwPolicy_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwPolicy_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwPolicy_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwPolicy_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwPolicy_get_CurrentProfile(This,profile) \
+ (This)->lpVtbl -> get_CurrentProfile(This,profile)
+
+#define INetFwPolicy_GetProfileByType(This,profileType,profile) \
+ (This)->lpVtbl -> GetProfileByType(This,profileType,profile)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwPolicy_get_CurrentProfile_Proxy(
+ INetFwPolicy * This,
+ /* [retval][out] */ INetFwProfile **profile);
+
+
+void __RPC_STUB INetFwPolicy_get_CurrentProfile_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwPolicy_GetProfileByType_Proxy(
+ INetFwPolicy * This,
+ /* [in] */ NET_FW_PROFILE_TYPE profileType,
+ /* [retval][out] */ INetFwProfile **profile);
+
+
+void __RPC_STUB INetFwPolicy_GetProfileByType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwPolicy_INTERFACE_DEFINED__ */
+
+
+#ifndef __INetFwMgr_INTERFACE_DEFINED__
+#define __INetFwMgr_INTERFACE_DEFINED__
+
+/* interface INetFwMgr */
+/* [dual][uuid][object] */
+
+
+EXTERN_C const IID IID_INetFwMgr;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("F7898AF5-CAC4-4632-A2EC-DA06E5111AF2")
+ INetFwMgr : public IDispatch
+ {
+ public:
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_LocalPolicy(
+ /* [retval][out] */ INetFwPolicy **localPolicy) = 0;
+
+ virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CurrentProfileType(
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE RestoreDefaults( void) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE IsPortAllowed(
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted) = 0;
+
+ virtual /* [id] */ HRESULT STDMETHODCALLTYPE IsIcmpTypeAllowed(
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted) = 0;
+
+ };
+
+#else /* C style interface */
+
+ typedef struct INetFwMgrVtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ INetFwMgr * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ INetFwMgr * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ INetFwMgr * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )(
+ INetFwMgr * This,
+ /* [out] */ UINT *pctinfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )(
+ INetFwMgr * This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo);
+
+ HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )(
+ INetFwMgr * This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId);
+
+ /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )(
+ INetFwMgr * This,
+ /* [in] */ DISPID dispIdMember,
+ /* [in] */ REFIID riid,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [out][in] */ DISPPARAMS *pDispParams,
+ /* [out] */ VARIANT *pVarResult,
+ /* [out] */ EXCEPINFO *pExcepInfo,
+ /* [out] */ UINT *puArgErr);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_LocalPolicy )(
+ INetFwMgr * This,
+ /* [retval][out] */ INetFwPolicy **localPolicy);
+
+ /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_CurrentProfileType )(
+ INetFwMgr * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *RestoreDefaults )(
+ INetFwMgr * This);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *IsPortAllowed )(
+ INetFwMgr * This,
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+ /* [id] */ HRESULT ( STDMETHODCALLTYPE *IsIcmpTypeAllowed )(
+ INetFwMgr * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+ END_INTERFACE
+ } INetFwMgrVtbl;
+
+ interface INetFwMgr
+ {
+ CONST_VTBL struct INetFwMgrVtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define INetFwMgr_QueryInterface(This,riid,ppvObject) \
+ (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
+
+#define INetFwMgr_AddRef(This) \
+ (This)->lpVtbl -> AddRef(This)
+
+#define INetFwMgr_Release(This) \
+ (This)->lpVtbl -> Release(This)
+
+
+#define INetFwMgr_GetTypeInfoCount(This,pctinfo) \
+ (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)
+
+#define INetFwMgr_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
+ (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)
+
+#define INetFwMgr_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
+ (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)
+
+#define INetFwMgr_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
+ (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)
+
+
+#define INetFwMgr_get_LocalPolicy(This,localPolicy) \
+ (This)->lpVtbl -> get_LocalPolicy(This,localPolicy)
+
+#define INetFwMgr_get_CurrentProfileType(This,profileType) \
+ (This)->lpVtbl -> get_CurrentProfileType(This,profileType)
+
+#define INetFwMgr_RestoreDefaults(This) \
+ (This)->lpVtbl -> RestoreDefaults(This)
+
+#define INetFwMgr_IsPortAllowed(This,imageFileName,ipVersion,portNumber,localAddress,ipProtocol,allowed,restricted) \
+ (This)->lpVtbl -> IsPortAllowed(This,imageFileName,ipVersion,portNumber,localAddress,ipProtocol,allowed,restricted)
+
+#define INetFwMgr_IsIcmpTypeAllowed(This,ipVersion,localAddress,type,allowed,restricted) \
+ (This)->lpVtbl -> IsIcmpTypeAllowed(This,ipVersion,localAddress,type,allowed,restricted)
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_get_LocalPolicy_Proxy(
+ INetFwMgr * This,
+ /* [retval][out] */ INetFwPolicy **localPolicy);
+
+
+void __RPC_STUB INetFwMgr_get_LocalPolicy_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [propget][id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_get_CurrentProfileType_Proxy(
+ INetFwMgr * This,
+ /* [retval][out] */ NET_FW_PROFILE_TYPE *profileType);
+
+
+void __RPC_STUB INetFwMgr_get_CurrentProfileType_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_RestoreDefaults_Proxy(
+ INetFwMgr * This);
+
+
+void __RPC_STUB INetFwMgr_RestoreDefaults_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_IsPortAllowed_Proxy(
+ INetFwMgr * This,
+ /* [in] */ BSTR imageFileName,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ LONG portNumber,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ NET_FW_IP_PROTOCOL ipProtocol,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+
+void __RPC_STUB INetFwMgr_IsPortAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+/* [id] */ HRESULT STDMETHODCALLTYPE INetFwMgr_IsIcmpTypeAllowed_Proxy(
+ INetFwMgr * This,
+ /* [in] */ NET_FW_IP_VERSION ipVersion,
+ /* [in] */ BSTR localAddress,
+ /* [in] */ BYTE type,
+ /* [out] */ VARIANT *allowed,
+ /* [out] */ VARIANT *restricted);
+
+
+void __RPC_STUB INetFwMgr_IsIcmpTypeAllowed_Stub(
+ IRpcStubBuffer *This,
+ IRpcChannelBuffer *_pRpcChannelBuffer,
+ PRPC_MESSAGE _pRpcMessage,
+ DWORD *_pdwStubPhase);
+
+
+
+#endif /* __INetFwMgr_INTERFACE_DEFINED__ */
+
+
+
+#ifndef __NetFwPublicTypeLib_LIBRARY_DEFINED__
+#define __NetFwPublicTypeLib_LIBRARY_DEFINED__
+
+/* library NetFwPublicTypeLib */
+/* [version][uuid] */
+
+
+
+
+
+
+
+
+
+
+
+
+
+EXTERN_C const IID LIBID_NetFwPublicTypeLib;
+
+EXTERN_C const CLSID CLSID_NetFwOpenPort;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("0CA545C6-37AD-4A6C-BF92-9F7610067EF5")
+NetFwOpenPort;
+#endif
+
+EXTERN_C const CLSID CLSID_NetFwAuthorizedApplication;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("EC9846B3-2762-4A6B-A214-6ACB603462D2")
+NetFwAuthorizedApplication;
+#endif
+
+EXTERN_C const CLSID CLSID_NetFwMgr;
+
+#ifdef __cplusplus
+
+class DECLSPEC_UUID("304CE942-6E39-40D8-943A-B913C40C9CD4")
+NetFwMgr;
+#endif
+#endif /* __NetFwPublicTypeLib_LIBRARY_DEFINED__ */
+
+/* Additional Prototypes for ALL interfaces */
+
+unsigned long __RPC_USER BSTR_UserSize( unsigned long *, unsigned long , BSTR * );
+unsigned char * __RPC_USER BSTR_UserMarshal( unsigned long *, unsigned char *, BSTR * );
+unsigned char * __RPC_USER BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );
+void __RPC_USER BSTR_UserFree( unsigned long *, BSTR * );
+
+unsigned long __RPC_USER VARIANT_UserSize( unsigned long *, unsigned long , VARIANT * );
+unsigned char * __RPC_USER VARIANT_UserMarshal( unsigned long *, unsigned char *, VARIANT * );
+unsigned char * __RPC_USER VARIANT_UserUnmarshal(unsigned long *, unsigned char *, VARIANT * );
+void __RPC_USER VARIANT_UserFree( unsigned long *, VARIANT * );
+
+/* end of Additional Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/Plugins/jingle/libjingle/talk/base/network.cc b/Plugins/jingle/libjingle/talk/base/network.cc new file mode 100644 index 0000000..d5a7d24 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/network.cc @@ -0,0 +1,381 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> +#include <cassert> +#include <cfloat> +#include <cmath> +#include <sstream> + +#ifdef POSIX +extern "C" { +#include <sys/socket.h> +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +} +#endif // POSIX + +#ifdef WIN32 +#include "talk/base/win32.h" +#include <Iphlpapi.h> +#endif + +#include "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/base/network.h" +#include "talk/base/socket.h" // this includes something that makes windows happy +#include "talk/base/stringencode.h" +#include "talk/base/time.h" +#include "talk/base/basicdefs.h" + +namespace { + +const double kAlpha = 0.5; // weight for data infinitely far in the past +const double kHalfLife = 2000; // half life of exponential decay (in ms) +const double kLog2 = 0.693147180559945309417; +const double kLambda = kLog2 / kHalfLife; + +// assume so-so quality unless data says otherwise +const double kDefaultQuality = talk_base::QUALITY_FAIR; + +typedef std::map<std::string,std::string> StrMap; + +void BuildMap(const StrMap& map, std::string& str) { + str.append("{"); + bool first = true; + for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { + if (!first) str.append(","); + str.append(i->first); + str.append("="); + str.append(i->second); + first = false; + } + str.append("}"); +} + +void ParseCheck(std::istringstream& ist, char ch) { + if (ist.get() != ch) + LOG(LERROR) << "Expecting '" << ch << "'"; +} + +std::string ParseString(std::istringstream& ist) { + std::string str; + int count = 0; + while (ist) { + char ch = ist.peek(); + if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { + break; + } else if (ch == '{') { + count += 1; + } else if (ch == '}') { + count -= 1; + if (count < 0) + LOG(LERROR) << "mismatched '{' and '}'"; + } + str.append(1, static_cast<char>(ist.get())); + } + return str; +} + +void ParseMap(const std::string& str, StrMap& map) { + if (str.size() == 0) + return; + std::istringstream ist(str); + ParseCheck(ist, '{'); + for (;;) { + std::string key = ParseString(ist); + ParseCheck(ist, '='); + std::string val = ParseString(ist); + map[key] = val; + if (ist.peek() == ',') + ist.get(); + else + break; + } + ParseCheck(ist, '}'); + if (ist.rdbuf()->in_avail() != 0) + LOG(LERROR) << "Unexpected characters at end"; +} + +#if 0 +const std::string TEST_MAP0_IN = ""; +const std::string TEST_MAP0_OUT = "{}"; +const std::string TEST_MAP1 = "{a=12345}"; +const std::string TEST_MAP2 = "{a=12345,b=67890}"; +const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}"; +const std::string TEST_MAP4 = "{a={d=12345,e=67890}}"; +const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}"; +const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}"; +const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}"; + +class MyTest { +public: + MyTest() { + test(TEST_MAP0_IN, TEST_MAP0_OUT); + test(TEST_MAP1, TEST_MAP1); + test(TEST_MAP2, TEST_MAP2); + test(TEST_MAP3, TEST_MAP3); + test(TEST_MAP4, TEST_MAP4); + test(TEST_MAP5, TEST_MAP5); + test(TEST_MAP6, TEST_MAP6); + test(TEST_MAP7, TEST_MAP7); + } + void test(const std::string& input, const std::string& exp_output) { + StrMap map; + ParseMap(input, map); + std::string output; + BuildMap(map, output); + LOG(INFO) << " ******** " << (output == exp_output); + } +}; + +static MyTest myTest; +#endif + +} // namespace + +namespace talk_base { + +#ifdef POSIX +void NetworkManager::CreateNetworks(std::vector<Network*>& networks) { + int fd; + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + PLOG(LERROR, errno) << "socket"; + return; + } + + struct ifconf ifc; + ifc.ifc_len = 64 * sizeof(struct ifreq); + ifc.ifc_buf = new char[ifc.ifc_len]; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + PLOG(LERROR, errno) << "ioctl"; + return; + } + assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq))); + + struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf); + struct ifreq* end = + reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len); + + while (ptr < end) { + if (strcmp(ptr->ifr_name, "lo")) { // Ignore the loopback device + struct sockaddr_in* inaddr = + reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr); + if (inaddr->sin_family == AF_INET) { + uint32 ip = ntohl(inaddr->sin_addr.s_addr); + networks.push_back(new Network(std::string(ptr->ifr_name), ip)); + } + } +#ifdef _SIZEOF_ADDR_IFREQ + ptr = reinterpret_cast<struct ifreq*>( + reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); +#else + ptr++; +#endif + } + + delete [] ifc.ifc_buf; + close(fd); +} +#endif + +#ifdef WIN32 +void NetworkManager::CreateNetworks(std::vector<Network*>& networks) { + IP_ADAPTER_INFO info_temp; + ULONG len = 0; + + if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) + return; + IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len]; + if (GetAdaptersInfo(infos, &len) != NO_ERROR) + return; + + int count = 0; + for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { + if (info->Type == MIB_IF_TYPE_LOOPBACK) + continue; + if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0) + continue; + + // In production, don't transmit the network name because of + // privacy concerns. Transmit a number instead. + + std::string name; +#if defined(PRODUCTION) + std::ostringstream ost; + ost << count; + name = ost.str(); + count++; +#else + name = info->Description; +#endif + + networks.push_back(new Network(name, + SocketAddress::StringToIP(info->IpAddressList.IpAddress.String))); + } + + delete infos; +} +#endif + +void NetworkManager::GetNetworks(std::vector<Network*>& result) { + std::vector<Network*> list; + CreateNetworks(list); + + for (uint32 i = 0; i < list.size(); ++i) { + NetworkMap::iterator iter = networks_.find(list[i]->name()); + + Network* network; + if (iter == networks_.end()) { + network = list[i]; + } else { + network = iter->second; + network->set_ip(list[i]->ip()); + delete list[i]; + } + + networks_[network->name()] = network; + result.push_back(network); + } +} + +std::string NetworkManager::GetState() { + StrMap map; + for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) + map[i->first] = i->second->GetState(); + + std::string str; + BuildMap(map, str); + return str; +} + +void NetworkManager::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { + std::string name = i->first; + std::string state = i->second; + + Network* network = new Network(name, 0); + network->SetState(state); + networks_[name] = network; + } +} + +Network::Network(const std::string& name, uint32 ip) + : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0), + exponential_numerator_(0), exponential_denominator_(0), + quality_(kDefaultQuality) { + + last_data_time_ = Time(); + + // TODO: seed the historical data with one data point based on the link speed + // metric from XP (4.0 if < 50, 3.0 otherwise). +} + +void Network::StartSession(NetworkSession* session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end()); + sessions_.push_back(session); +} + +void Network::StopSession(NetworkSession* session) { + SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session); + if (iter != sessions_.end()) + sessions_.erase(iter); +} + +void Network::EstimateQuality() { + uint32 now = Time(); + + // Add new data points for the current time. + for (uint32 i = 0; i < sessions_.size(); ++i) { + if (sessions_[i]->HasQuality()) + AddDataPoint(now, sessions_[i]->GetCurrentQuality()); + } + + // Construct the weighted average using both uniform and exponential weights. + + double exp_shift = exp(-kLambda * (now - last_data_time_)); + double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; + double denominator = uniform_denominator_ + exp_shift * exponential_denominator_; + + if (denominator < DBL_EPSILON) + quality_ = kDefaultQuality; + else + quality_ = numerator / denominator; +} + +std::string Network::ToString() const { + std::stringstream ss; + // Print out the first space-terminated token of the network name, plus + // the IP address. + ss << "Net[" << name_.substr(0, name_.find(' ')) + << ":" << SocketAddress::IPToString(ip_) << "]"; + return ss.str(); +} + +void Network::AddDataPoint(uint32 time, double quality) { + uniform_numerator_ += kAlpha * quality; + uniform_denominator_ += kAlpha; + + double exp_shift = exp(-kLambda * (time - last_data_time_)); + exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_; + exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_; + + last_data_time_ = time; +} + +std::string Network::GetState() { + StrMap map; + map["lt"] = talk_base::ToString<uint32>(last_data_time_); + map["un"] = talk_base::ToString<double>(uniform_numerator_); + map["ud"] = talk_base::ToString<double>(uniform_denominator_); + map["en"] = talk_base::ToString<double>(exponential_numerator_); + map["ed"] = talk_base::ToString<double>(exponential_denominator_); + + std::string str; + BuildMap(map, str); + return str; +} + +void Network::SetState(std::string str) { + StrMap map; + ParseMap(str, map); + + last_data_time_ = FromString<uint32>(map["lt"]); + uniform_numerator_ = FromString<double>(map["un"]); + uniform_denominator_ = FromString<double>(map["ud"]); + exponential_numerator_ = FromString<double>(map["en"]); + exponential_denominator_ = FromString<double>(map["ed"]); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/network.h b/Plugins/jingle/libjingle/talk/base/network.h new file mode 100644 index 0000000..52785ba --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/network.h @@ -0,0 +1,139 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_NETWORK_H__ +#define TALK_BASE_NETWORK_H__ + +#include <deque> +#include <map> +#include <string> +#include <vector> + +#include "talk/base/basictypes.h" + +namespace talk_base { + +class Network; +class NetworkSession; + +// Keeps track of the available network interfaces over time so that quality +// information can be aggregated and recorded. +class NetworkManager { +public: + + // Updates and returns the current list of networks available on this machine. + // This version will make sure that repeated calls return the same object for + // a given network, so that quality is tracked appropriately. + void GetNetworks(std::vector<Network*>& networks); + + // Reads and writes the state of the quality database in a string format. + std::string GetState(); + void SetState(std::string str); + + // Creates a network object for each network available on the machine. + static void CreateNetworks(std::vector<Network*>& networks); + +private: + typedef std::map<std::string,Network*> NetworkMap; + + NetworkMap networks_; +}; + +// Represents a Unix-type network interface, with a name and single address. +// It also includes the ability to track and estimate quality. +class Network { +public: + Network(const std::string& name, uint32 ip); + + // Returns the OS name of this network. This is considered the primary key + // that identifies each network. + const std::string& name() const { return name_; } + + // Identifies the current IP address used by this network. + uint32 ip() const { return ip_; } + void set_ip(uint32 ip) { ip_ = ip; } + + // Updates the list of sessions that are ongoing. + void StartSession(NetworkSession* session); + void StopSession(NetworkSession* session); + + // Re-computes the estimate of near-future quality based on the information + // as of this exact moment. + void EstimateQuality(); + + // Returns the current estimate of the near-future quality of connections + // that use this local interface. + double quality() { return quality_; } + + // Debugging description of this network + std::string ToString() const; + +private: + typedef std::vector<NetworkSession*> SessionList; + + std::string name_; + uint32 ip_; + SessionList sessions_; + double uniform_numerator_; + double uniform_denominator_; + double exponential_numerator_; + double exponential_denominator_; + uint32 last_data_time_; + double quality_; + + // Updates the statistics maintained to include the given estimate. + void AddDataPoint(uint32 time, double quality); + + // Converts the internal state to and from a string. This is used to record + // quality information into a permanent store. + void SetState(std::string str); + std::string GetState(); + + friend class NetworkManager; +}; + +// Represents a session that is in progress using a particular network and can +// provide data about the quality of the network at any given moment. +class NetworkSession { +public: + // Determines whether this session has an estimate at this moment. We will + // only call GetCurrentQuality when this returns true. + virtual bool HasQuality() = 0; + + // Returns an estimate of the quality at this exact moment. The result should + // be a MOS (mean opinion score) value. + virtual float GetCurrentQuality() = 0; + +}; + +const double QUALITY_BAD = 3.0; +const double QUALITY_FAIR = 3.35; +const double QUALITY_GOOD = 3.7; + +} // namespace talk_base + +#endif // TALK_BASE_NETWORK_H__ diff --git a/Plugins/jingle/libjingle/talk/base/openssladapter.cc b/Plugins/jingle/libjingle/talk/base/openssladapter.cc new file mode 100644 index 0000000..1d4f02c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/openssladapter.cc @@ -0,0 +1,809 @@ +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/openssladapter.h" +#include "talk/base/stringutils.h" +#include "talk/base/Equifax_Secure_Global_eBusiness_CA-1.h" + +////////////////////////////////////////////////////////////////////// +// StreamBIO +////////////////////////////////////////////////////////////////////// + +#if 0 +static int stream_write(BIO* h, const char* buf, int num); +static int stream_read(BIO* h, char* buf, int size); +static int stream_puts(BIO* h, const char* str); +static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2); +static int stream_new(BIO* h); +static int stream_free(BIO* data); + +static BIO_METHOD methods_stream = { + BIO_TYPE_BIO, + "stream", + stream_write, + stream_read, + stream_puts, + 0, + stream_ctrl, + stream_new, + stream_free, + NULL, +}; + +BIO_METHOD* BIO_s_stream() { return(&methods_stream); } + +BIO* BIO_new_stream(StreamInterface* stream) { + BIO* ret = BIO_new(BIO_s_stream()); + if (ret == NULL) + return NULL; + ret->ptr = stream; + return ret; +} + +static int stream_new(BIO* b) { + b->shutdown = 0; + b->init = 1; + b->num = 0; // 1 means end-of-stream + b->ptr = 0; + return 1; +} + +static int stream_free(BIO* b) { + if (b == NULL) + return 0; + return 1; +} + +static int stream_read(BIO* b, char* out, int outl) { + if (!out) + return -1; + StreamInterface* stream = static_cast<StreamInterface*>(b->ptr); + BIO_clear_retry_flags(b); + size_t read; + int error; + StreamResult result = stream->Read(out, outl, &read, &error); + if (result == SR_SUCCESS) { + return read; + } else if (result == SR_EOS) { + b->num = 1; + } else if (result == SR_BLOCK) { + BIO_set_retry_read(b); + } + return -1; +} + +static int stream_write(BIO* b, const char* in, int inl) { + if (!in) + return -1; + StreamInterface* stream = static_cast<StreamInterface*>(b->ptr); + BIO_clear_retry_flags(b); + size_t written; + int error; + StreamResult result = stream->Write(in, inl, &written, &error); + if (result == SR_SUCCESS) { + return written; + } else if (result == SR_BLOCK) { + BIO_set_retry_write(b); + } + return -1; +} + +static int stream_puts(BIO* b, const char* str) { + return stream_write(b, str, strlen(str)); +} + +static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) { + UNUSED(num); + UNUSED(ptr); + + switch (cmd) { + case BIO_CTRL_RESET: + return 0; + case BIO_CTRL_EOF: + return b->num; + case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: + return 0; + case BIO_CTRL_FLUSH: + return 1; + default: + return 0; + } +} +#endif + +////////////////////////////////////////////////////////////////////// +// SocketBIO +////////////////////////////////////////////////////////////////////// + +static int socket_write(BIO* h, const char* buf, int num); +static int socket_read(BIO* h, char* buf, int size); +static int socket_puts(BIO* h, const char* str); +static long socket_ctrl(BIO* h, int cmd, long arg1, void* arg2); +static int socket_new(BIO* h); +static int socket_free(BIO* data); + +static BIO_METHOD methods_socket = { + BIO_TYPE_BIO, + "socket", + socket_write, + socket_read, + socket_puts, + 0, + socket_ctrl, + socket_new, + socket_free, + NULL, +}; + +BIO_METHOD* BIO_s_socket2() { return(&methods_socket); } + +BIO* BIO_new_socket(talk_base::AsyncSocket* socket) { + BIO* ret = BIO_new(BIO_s_socket2()); + if (ret == NULL) { + return NULL; + } + ret->ptr = socket; + return ret; +} + +static int socket_new(BIO* b) { + b->shutdown = 0; + b->init = 1; + b->num = 0; // 1 means socket closed + b->ptr = 0; + return 1; +} + +static int socket_free(BIO* b) { + if (b == NULL) + return 0; + return 1; +} + +static int socket_read(BIO* b, char* out, int outl) { + if (!out) + return -1; + talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr); + BIO_clear_retry_flags(b); + int result = socket->Recv(out, outl); + if (result > 0) { + return result; + } else if (result == 0) { + b->num = 1; + } else if (socket->IsBlocking()) { + BIO_set_retry_read(b); + } + return -1; +} + +static int socket_write(BIO* b, const char* in, int inl) { + if (!in) + return -1; + talk_base::AsyncSocket* socket = static_cast<talk_base::AsyncSocket*>(b->ptr); + BIO_clear_retry_flags(b); + int result = socket->Send(in, inl); + if (result > 0) { + return result; + } else if (socket->IsBlocking()) { + BIO_set_retry_write(b); + } + return -1; +} + +static int socket_puts(BIO* b, const char* str) { + return socket_write(b, str, strlen(str)); +} + +static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) { + UNUSED(num); + UNUSED(ptr); + + switch (cmd) { + case BIO_CTRL_RESET: + return 0; + case BIO_CTRL_EOF: + return b->num; + case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: + return 0; + case BIO_CTRL_FLUSH: + return 1; + default: + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////// +// OpenSSLAdapter +///////////////////////////////////////////////////////////////////////////// + +namespace talk_base { + +OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket) + : SSLAdapter(socket), + state_(SSL_NONE), + ssl_read_needs_write_(false), + ssl_write_needs_read_(false), + restartable_(false), + ssl_(NULL), ssl_ctx_(NULL) { +} + +OpenSSLAdapter::~OpenSSLAdapter() { + Cleanup(); +} + +int +OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) { + if (state_ != SSL_NONE) + return -1; + + ssl_host_name_ = hostname; + restartable_ = restartable; + + if (socket_->GetState() != Socket::CS_CONNECTED) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +int +OpenSSLAdapter::BeginSSL() { + LOG(LS_INFO) << "BeginSSL: " << ssl_host_name_; + ASSERT(state_ == SSL_CONNECTING); + + int err = 0; + BIO* bio = NULL; + + // First set up the context + if (!ssl_ctx_) + ssl_ctx_ = SetupSSLContext(); + + if (!ssl_ctx_) { + err = -1; + goto ssl_error; + } + + bio = BIO_new_socket(static_cast<talk_base::AsyncSocketAdapter*>(socket_)); + if (!bio) { + err = -1; + goto ssl_error; + } + + ssl_ = SSL_new(ssl_ctx_); + if (!ssl_) { + err = -1; + goto ssl_error; + } + + SSL_set_app_data(ssl_, this); + + SSL_set_bio(ssl_, bio, bio); + SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + // the SSL object owns the bio now + bio = NULL; + + // Do the connect + err = ContinueSSL(); + if (err != 0) + goto ssl_error; + + return err; + +ssl_error: + Cleanup(); + if (bio) + BIO_free(bio); + + return err; +} + +int +OpenSSLAdapter::ContinueSSL() { + LOG(LS_INFO) << "ContinueSSL"; + ASSERT(state_ == SSL_CONNECTING); + + int code = SSL_connect(ssl_); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + LOG(LS_INFO) << " -- success"; + + if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) { + LOG(LS_ERROR) << "TLS post connection check failed"; + // make sure we close the socket + Cleanup(); + // The connect failed so return -1 to shut down the socket + return -1; + } + + state_ = SSL_CONNECTED; + AsyncSocketAdapter::OnConnectEvent(this); +#if 0 // TODO: worry about this + // Don't let ourselves go away during the callbacks + PRefPtr<OpenSSLAdapter> lock(this); + LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(this); + LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(this); +#endif + break; + + case SSL_ERROR_WANT_READ: + LOG(LS_INFO) << " -- error want read"; + break; + + case SSL_ERROR_WANT_WRITE: + LOG(LS_INFO) << " -- error want write"; + break; + + case SSL_ERROR_ZERO_RETURN: + default: + LOG(LS_INFO) << " -- error " << code; + return (code != 0) ? code : -1; + } + + return 0; +} + +void +OpenSSLAdapter::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "SChannelAdapter::Error(" + << context << ", " << err << ")"; + state_ = SSL_ERROR; + SetError(err); + if (signal) + AsyncSocketAdapter::OnCloseEvent(this, err); +} + +void +OpenSSLAdapter::Cleanup() { + LOG(LS_INFO) << "Cleanup"; + + state_ = SSL_NONE; + ssl_read_needs_write_ = false; + ssl_write_needs_read_ = false; + + if (ssl_) { + SSL_free(ssl_); + ssl_ = NULL; + } + + if (ssl_ctx_) { + SSL_CTX_free(ssl_ctx_); + ssl_ctx_ = NULL; + } +} + +// +// AsyncSocket Implementation +// + +int +OpenSSLAdapter::Send(const void* pv, size_t cb) { + //LOG(LS_INFO) << "OpenSSLAdapter::Send(" << cb << ")"; + + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Send(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + // OpenSSL will return an error if we try to write zero bytes + if (cb == 0) + return 0; + + ssl_write_needs_read_ = false; + + int code = SSL_write(ssl_, pv, cb); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + //LOG(LS_INFO) << " -- success"; + return code; + case SSL_ERROR_WANT_READ: + //LOG(LS_INFO) << " -- error want read"; + ssl_write_needs_read_ = true; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_WANT_WRITE: + //LOG(LS_INFO) << " -- error want write"; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_ZERO_RETURN: + //LOG(LS_INFO) << " -- remote side closed"; + SetError(EWOULDBLOCK); + // do we need to signal closure? + break; + default: + //LOG(LS_INFO) << " -- error " << code; + Error("SSL_write", (code ? code : -1), false); + break; + } + + return SOCKET_ERROR; +} + +int +OpenSSLAdapter::Recv(void* pv, size_t cb) { + //LOG(LS_INFO) << "OpenSSLAdapter::Recv(" << cb << ")"; + switch (state_) { + + case SSL_NONE: + return AsyncSocketAdapter::Recv(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + // Don't trust OpenSSL with zero byte reads + if (cb == 0) + return 0; + + ssl_read_needs_write_ = false; + + int code = SSL_read(ssl_, pv, cb); + switch (SSL_get_error(ssl_, code)) { + case SSL_ERROR_NONE: + //LOG(LS_INFO) << " -- success"; + return code; + case SSL_ERROR_WANT_READ: + //LOG(LS_INFO) << " -- error want read"; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_WANT_WRITE: + //LOG(LS_INFO) << " -- error want write"; + ssl_read_needs_write_ = true; + SetError(EWOULDBLOCK); + break; + case SSL_ERROR_ZERO_RETURN: + //LOG(LS_INFO) << " -- remote side closed"; + SetError(EWOULDBLOCK); + // do we need to signal closure? + break; + default: + //LOG(LS_INFO) << " -- error " << code; + Error("SSL_read", (code ? code : -1), false); + break; + } + + return SOCKET_ERROR; +} + +int +OpenSSLAdapter::Close() { + Cleanup(); + state_ = restartable_ ? SSL_WAIT : SSL_NONE; + return AsyncSocketAdapter::Close(); +} + +Socket::ConnState +OpenSSLAdapter::GetState() const { + //if (signal_close_) + // return CS_CONNECTED; + ConnState state = socket_->GetState(); + if ((state == CS_CONNECTED) + && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING))) + state = CS_CONNECTING; + return state; +} + +void +OpenSSLAdapter::OnConnectEvent(AsyncSocket* socket) { + LOG(LS_INFO) << "OpenSSLAdapter::OnConnectEvent"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + AsyncSocketAdapter::OnConnectEvent(socket); + return; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + AsyncSocketAdapter::OnCloseEvent(socket, err); + } +} + +void +OpenSSLAdapter::OnReadEvent(AsyncSocket* socket) { + //LOG(LS_INFO) << "OpenSSLAdapter::OnReadEvent"; + + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + return; + } + + if (state_ != SSL_CONNECTED) + return; + + // Don't let ourselves go away during the callbacks + //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this + if (ssl_write_needs_read_) { + //LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(socket); + } + + //LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(socket); +} + +void +OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) { + //LOG(LS_INFO) << "OpenSSLAdapter::OnWriteEvent"; + + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnWriteEvent(socket); + return; + } + + if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + return; + } + + if (state_ != SSL_CONNECTED) + return; + + // Don't let ourselves go away during the callbacks + //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this + + if (ssl_read_needs_write_) { + //LOG(LS_INFO) << " -- onStreamReadable"; + AsyncSocketAdapter::OnReadEvent(socket); + } + + //LOG(LS_INFO) << " -- onStreamWriteable"; + AsyncSocketAdapter::OnWriteEvent(socket); +} + +void +OpenSSLAdapter::OnCloseEvent(AsyncSocket* socket, int err) { + LOG(LS_INFO) << "OpenSSLAdapter::OnCloseEvent(" << err << ")"; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +// This code is taken from the "Network Security with OpenSSL" +// sample in chapter 5 +bool +OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) { + if (!host) + return false; + + // Checking the return from SSL_get_peer_certificate here is not strictly + // necessary. With our setup, it is not possible for it to return + // NULL. However, it is good form to check the return. + X509* certificate = SSL_get_peer_certificate(ssl); + if (!certificate) + return false; + +#ifdef _DEBUG + { + LOG(LS_INFO) << "Certificate from server:"; + BIO* mem = BIO_new(BIO_s_mem()); + X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER); + BIO_write(mem, "\0", 1); + char* buffer; + BIO_get_mem_data(mem, &buffer); + LOG(LS_INFO) << buffer; + BIO_free(mem); + + char* cipher_description = + SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128); + LOG(LS_INFO) << "Cipher: " << cipher_description; + OPENSSL_free(cipher_description); + } +#endif + + bool ok = false; + int extension_count = X509_get_ext_count(certificate); + for (int i = 0; i < extension_count; ++i) { + X509_EXTENSION* extension = X509_get_ext(certificate, i); + int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); + + if (extension_nid == NID_subject_alt_name) { + X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); + if (!meth) + break; + + void* ext_str = NULL; + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL + const unsigned char **ext_value_data = (const_cast<const unsigned char **> + (&extension->value->data)); +#else + unsigned char **ext_value_data = &extension->value->data; +#endif + + if (meth->it) { + ext_str = ASN1_item_d2i(NULL, ext_value_data, extension->value->length, + ASN1_ITEM_ptr(meth->it)); + } else { + ext_str = meth->d2i(NULL, ext_value_data, extension->value->length); + } + + STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL); + for (int j = 0; j < sk_CONF_VALUE_num(value); ++j) { + CONF_VALUE* nval = sk_CONF_VALUE_value(value, j); + if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) { + ok = true; + break; + } + } + } + if (ok) + break; + } + + char data[256]; + X509_name_st* subject; + if (!ok + && (subject = X509_get_subject_name(certificate)) + && (X509_NAME_get_text_by_NID(subject, NID_commonName, + data, sizeof(data)) > 0)) { + data[sizeof(data)-1] = 0; + if (_stricmp(data, host) == 0) + ok = true; + } + + X509_free(certificate); + + if (!ok && ignore_bad_cert()) { + LOG(LS_WARNING) << "TLS certificate check FAILED. " + << "Allowing connection anyway."; + ok = true; + } + + if (ok) + ok = (SSL_get_verify_result(ssl) == X509_V_OK); + + if (!ok && ignore_bad_cert()) { + LOG(LS_INFO) << "Other TLS post connection checks failed."; + ok = true; + } + + return ok; +} + +#if _DEBUG + +// We only use this for tracing and so it is only needed in debug mode + +void +OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) { + const char* str = "undefined"; + int w = where & ~SSL_ST_MASK; + if (w & SSL_ST_CONNECT) { + str = "SSL_connect"; + } else if (w & SSL_ST_ACCEPT) { + str = "SSL_accept"; + } + if (where & SSL_CB_LOOP) { + LOG(LS_INFO) << str << ":" << SSL_state_string_long(s); + } else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + LOG(LS_INFO) << "SSL3 alert " << str + << ":" << SSL_alert_type_string_long(ret) + << ":" << SSL_alert_desc_string_long(ret); + } else if (where & SSL_CB_EXIT) { + if (ret == 0) { + LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s); + } else if (ret < 0) { + LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s); + } + } +} + +#endif // _DEBUG + +int +OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { +#if _DEBUG + if (!ok) { + char data[256]; + X509* cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int err = X509_STORE_CTX_get_error(store); + + LOG(LS_INFO) << "Error with certificate at depth: " << depth; + X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data)); + LOG(LS_INFO) << " issuer = " << data; + X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data)); + LOG(LS_INFO) << " subject = " << data; + LOG(LS_INFO) << " err = " << err + << ":" << X509_verify_cert_error_string(err); + } +#endif + + // Get our stream pointer from the store + SSL* ssl = reinterpret_cast<SSL*>( + X509_STORE_CTX_get_ex_data(store, + SSL_get_ex_data_X509_STORE_CTX_idx())); + + OpenSSLAdapter* stream = + reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl)); + + if (!ok && stream->ignore_bad_cert()) { + LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; + ok = 1; + } + + return ok; +} + +SSL_CTX* +OpenSSLAdapter::SetupSSLContext() { + SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method()); + if (ctx == NULL) + return NULL; + + // Add the root cert to the SSL context +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL + const unsigned char* cert_buffer +#else + unsigned char* cert_buffer +#endif + = EquifaxSecureGlobalEBusinessCA1_certificate; + size_t cert_buffer_len = sizeof(EquifaxSecureGlobalEBusinessCA1_certificate); + X509* cert = d2i_X509(NULL, &cert_buffer, cert_buffer_len); + if (cert == NULL) { + SSL_CTX_free(ctx); + return NULL; + } + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert)) { + X509_free(cert); + SSL_CTX_free(ctx); + return NULL; + } + +#ifdef _DEBUG + SSL_CTX_set_info_callback(ctx, SSLInfoCallback); +#endif + + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback); + SSL_CTX_set_verify_depth(ctx, 4); + SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + + return ctx; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/openssladapter.h b/Plugins/jingle/libjingle/talk/base/openssladapter.h new file mode 100644 index 0000000..b794a7b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/openssladapter.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_OPENSSLADAPTER_H__ +#define TALK_BASE_OPENSSLADAPTER_H__ + +#include <string> +#include "talk/base/ssladapter.h" + +typedef struct ssl_st SSL; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct x509_store_ctx_st X509_STORE_CTX; + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +class OpenSSLAdapter : public SSLAdapter { +public: + OpenSSLAdapter(AsyncSocket* socket); + virtual ~OpenSSLAdapter(); + + virtual int StartSSL(const char* hostname, bool restartable); + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + virtual int Close(); + + // Note that the socket returns ST_CONNECTING while SSL is being negotiated. + virtual ConnState GetState() const; + +protected: + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void OnReadEvent(AsyncSocket* socket); + virtual void OnWriteEvent(AsyncSocket* socket); + virtual void OnCloseEvent(AsyncSocket* socket, int err); + +private: + enum SSLState { + SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR + }; + + int BeginSSL(); + int ContinueSSL(); + void Error(const char* context, int err, bool signal = true); + void Cleanup(); + + bool SSLPostConnectionCheck(SSL* ssl, const char* host); +#if _DEBUG + static void SSLInfoCallback(const SSL* s, int where, int ret); +#endif // !_DEBUG + static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); + + static SSL_CTX* SetupSSLContext(); + + SSLState state_; + bool ssl_read_needs_write_; + bool ssl_write_needs_read_; + // If true, socket will retain SSL configuration after Close. + bool restartable_; + + SSL* ssl_; + SSL_CTX* ssl_ctx_; + std::string ssl_host_name_; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_OPENSSLADAPTER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/pathutils.cc b/Plugins/jingle/libjingle/talk/base/pathutils.cc new file mode 100644 index 0000000..4a77879 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/pathutils.cc @@ -0,0 +1,426 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef WIN32 +#include "talk/base/win32.h" +#include <shellapi.h> +#include <shlobj.h> +#include <tchar.h> +#endif // WIN32 + +#include "talk/base/common.h" +#include "talk/base/pathutils.h" +#include "talk/base/stringutils.h" +#include "talk/base/urlencode.h" + +namespace talk_base { + +std::string const EMPTY_STR = ""; + +// EXT_DELIM separates a file basename from extension +const char EXT_DELIM = '.'; + +// FOLDER_DELIMS separate folder segments and the filename +const char* const FOLDER_DELIMS = "/\\"; + +// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform +#if WIN32 +const char DEFAULT_FOLDER_DELIM = '\\'; +#else // !WIN32 +const char DEFAULT_FOLDER_DELIM = '/'; +#endif // !WIN32 + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa +/////////////////////////////////////////////////////////////////////////////// + +bool Pathname::IsFolderDelimiter(char ch) { + return (NULL != ::strchr(FOLDER_DELIMS, ch)); +} + +Pathname::Pathname() + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { +} + +Pathname::Pathname(const std::string& pathname) + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { + SetPathname(pathname); +} + +void Pathname::SetFolderDelimiter(char delimiter) { + ASSERT(IsFolderDelimiter(delimiter)); + folder_delimiter_ = delimiter; +} + +void Pathname::Normalize() { + for (size_t i=0; i<folder_.length(); ++i) { + if (IsFolderDelimiter(folder_[i])) { + folder_[i] = folder_delimiter_; + } + } +} + +void Pathname::clear() { + folder_.clear(); + basename_.clear(); + extension_.clear(); +} + +std::string Pathname::pathname() const { + std::string pathname(folder_); + pathname.append(basename_); + pathname.append(extension_); + return pathname; +} + +std::string Pathname::url() const { + std::string s = "file://"; + for (size_t i=0; i<folder_.length(); ++i) { + if (i == 1 && folder_[i] == ':') // drive letter + s += '|'; + else if (IsFolderDelimiter(folder_[i])) + s += '/'; + else + s += folder_[i]; + } + s += basename_; + s += extension_; + return UrlEncodeString(s); +} + +void Pathname::SetPathname(const std::string &pathname) { + std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS); + if (pos != std::string::npos) { + SetFolder(pathname.substr(0, pos + 1)); + SetFilename(pathname.substr(pos + 1)); + } else { + SetFolder(EMPTY_STR); + SetFilename(pathname); + } +} + +void Pathname::AppendPathname(const Pathname& pathname) { + std::string full_pathname(folder_); + full_pathname.append(pathname.pathname()); + SetPathname(full_pathname); +} + +std::string Pathname::folder() const { + return folder_; +} + +std::string Pathname::folder_name() const { + std::string::size_type pos = std::string::npos; + if (folder_.size() >= 2) { + pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); + } + if (pos != std::string::npos) { + return folder_.substr(pos + 1); + } else { + return folder_; + } +} + +std::string Pathname::parent_folder() const { + std::string::size_type pos = std::string::npos; + if (folder_.size() >= 2) { + pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); + } + if (pos != std::string::npos) { + return folder_.substr(0, pos + 1); + } else { + return EMPTY_STR; + } +} + +void Pathname::SetFolder(const std::string& folder) { + folder_.assign(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +void Pathname::AppendFolder(const std::string& folder) { + folder_.append(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +std::string Pathname::basename() const { + return basename_; +} + +void Pathname::SetBasename(const std::string& basename) { + ASSERT(basename.find_first_of(FOLDER_DELIMS) == std::string::npos); + basename_.assign(basename); +} + +std::string Pathname::extension() const { + return extension_; +} + +void Pathname::SetExtension(const std::string& extension) { + ASSERT(extension.find_first_of(FOLDER_DELIMS) == std::string::npos); + ASSERT(extension.find_first_of(EXT_DELIM, 1) == std::string::npos); + extension_.assign(extension); + // Ensure extension begins with the extension delimiter + if (!extension_.empty() && (extension_[0] != EXT_DELIM)) { + extension_.insert(extension_.begin(), EXT_DELIM); + } +} + +std::string Pathname::filename() const { + std::string filename(basename_); + filename.append(extension_); + return filename; +} + +void Pathname::SetFilename(const std::string& filename) { + std::string::size_type pos = filename.rfind(EXT_DELIM); + if ((pos == std::string::npos) || (pos == 0)) { + SetBasename(filename); + SetExtension(EMPTY_STR); + } else { + SetBasename(filename.substr(0, pos)); + SetExtension(filename.substr(pos)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CreateUniqueFile +/////////////////////////////////////////////////////////////////////////////// + +std::string g_organization_name; +std::string g_application_name; + +void SetOrganizationName(const std::string& organization) { + g_organization_name = organization; +} + +void SetApplicationName(const std::string& application) { + g_application_name = application; +} + +bool CreateFolder(const talk_base::Pathname& path) { +#ifdef WIN32 + if (!path.filename().empty()) + return false; + + std::wstring pathname16 = ToUtf16(path.pathname()); + if (!pathname16.empty() && (pathname16[0] != '\\')) { + pathname16 = L"\\\\?\\" + pathname16; + } + + DWORD res = ::GetFileAttributes(pathname16.c_str()); + if (res != INVALID_FILE_ATTRIBUTES) { + // Something exists at this location, check if it is a directory + return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0); + } else if ((GetLastError() != ERROR_FILE_NOT_FOUND) + && (GetLastError() != ERROR_PATH_NOT_FOUND)) { + // Unexpected error + return false; + } + + // Directory doesn't exist, look up one directory level + if (!path.parent_folder().empty()) { + talk_base::Pathname parent(path); + parent.SetFolder(path.parent_folder()); + if (!CreateFolder(parent)) { + return false; + } + } + + return (::CreateDirectory(pathname16.c_str(), NULL) != 0); +#else // !WIN32 + return false; +#endif // !WIN32 +} + +bool FinishPath(talk_base::Pathname& path, bool create, + const std::string& append) { + if (!append.empty()) { + path.AppendFolder(append); + } + if (create && !CreateFolder(path)) + return false; + return true; +} + +bool GetTemporaryFolder(talk_base::Pathname& path, bool create, + const std::string& append) { + ASSERT(!g_application_name.empty()); +#ifdef WIN32 + TCHAR buffer[MAX_PATH + 1]; + if (!::GetTempPath(ARRAY_SIZE(buffer), buffer)) + return false; + if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + size_t len = strlen(buffer); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + __T("\\")); + } + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + ToUtf16(g_application_name).c_str()); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + __T("\\")); + } + if (len >= ARRAY_SIZE(buffer) - 1) + return false; + path.clear(); + path.SetFolder(ToUtf8(buffer)); + return FinishPath(path, create, append); +#else // !WIN32 + return false; +#endif // !WIN32 +} + +bool GetAppDataFolder(talk_base::Pathname& path, bool create, + const std::string& append) { + ASSERT(!g_organization_name.empty()); + ASSERT(!g_application_name.empty()); +#ifdef WIN32 + TCHAR buffer[MAX_PATH + 1]; + if (!::SHGetSpecialFolderPath(NULL, buffer, CSIDL_LOCAL_APPDATA, TRUE)) + return false; + if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + size_t len = talk_base::strcatn(buffer, ARRAY_SIZE(buffer), _T("\\")); + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + ToUtf16(g_organization_name).c_str()); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + __T("\\")); + } + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + ToUtf16(g_application_name).c_str()); + if ((len > 0) && (buffer[len-1] != __T('\\'))) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + __T("\\")); + } + if (len >= ARRAY_SIZE(buffer) - 1) + return false; + path.clear(); + path.SetFolder(ToUtf8(buffer)); + return FinishPath(path, create, append); +#else // !WIN32 + return false; +#endif // !WIN32 +} + +bool CleanupTemporaryFolder() { +#ifdef WIN32 + talk_base::Pathname temp_path; + if (!GetTemporaryFolder(temp_path, false, "")) + return false; + + std::wstring temp_path16 = ToUtf16(temp_path.pathname()); + temp_path16.append(1, '*'); + temp_path16.append(1, '\0'); + + SHFILEOPSTRUCT file_op = { 0 }; + file_op.wFunc = FO_DELETE; + file_op.pFrom = temp_path16.c_str(); + file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; + return (0 == SHFileOperation(&file_op)); +#else // !WIN32 + return false; +#endif // !WIN32 +} +#if 0 +bool CreateUnijqueFile(talk_base::Pathname& path, bool create_empty) { +#ifdef WIN32 + // If not folder is supplied, use the temporary folder + if (path.folder().empty()) { + talk_base::Pathname temp_path; + if (!GetTemporaryFolder(temp_path, true, "")) { + + return false; + } + path.SetFolder(temp_path.folder()); + } + printf("path: %s\n", path.pathname()); + // If not filename is supplied, use a temporary name + if (path.filename().empty()) { + TCHAR filename[MAX_PATH]; + std::wstring folder((ToUtf16)(path.folder())); + if (!::GetTempFileName(folder.c_str(), __T("gt"), 0, filename)) + return false; + ASSERT(wcsncmp(folder.c_str(), filename, folder.length()) == 0); + path.SetFilename(ToUtf8(filename + folder.length())); + if (!create_empty) { + VERIFY(::DeleteFile(ToUtf16(path.pathname()).c_str()) != FALSE); + } + return true; + } + // Otherwise, create a unique name based on the given filename + // foo.txt -> foo-N.txt + const std::string basename = path.basename(); + const size_t MAX_VERSION = 100; + size_t version = 0; + while (version < MAX_VERSION) { + std::string pathname = path.pathname(); + std::wstring pathname16 = ToUtf16(pathname).c_str(); + + if (pathname16[0] != __T('\\')) + pathname16 = __T("\\\\?\\") + pathname16; + + HANDLE hfile = CreateFile(pathname16.c_str(), GENERIC_WRITE, 0, + NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + CloseHandle(hfile); + if (!create_empty) { + VERIFY(::DeleteFile(pathname16.c_str()) != FALSE); + } + return true; + } else { + int err = GetLastError(); + if (err != ERROR_FILE_EXISTS && err != ERROR_ACCESS_DENIED) { + return false; + } + } + + version += 1; + char version_base[MAX_PATH]; + talk_base::sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u", + basename.c_str(), version); + path.SetBasename(version_base); + } + return false; +#else // !WIN32 + // TODO: Make this better. + path.SetBasename("/tmp/temp-1"); +#endif // !WIN32 +} +#endif +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/pathutils.h b/Plugins/jingle/libjingle/talk/base/pathutils.h new file mode 100644 index 0000000..eb33edf --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/pathutils.h @@ -0,0 +1,110 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_PATHUTILS_H__ +#define TALK_BASE_PATHUTILS_H__ + +#include <string> + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa. +// +// To establish consistent terminology, a filename never contains a folder +// component. A folder never contains a filename. A pathname may include +// a folder and/or filename component. Here are some examples: +// +// pathname() /home/john/example.txt +// folder() /home/john/ +// filename() example.txt +// parent_folder() /home/ +// folder_name() john/ +// basename() example +// extension() .txt +// +// Basename may begin, end, and/or include periods, but no folder delimiters. +// If extension exists, it consists of a period followed by zero or more +// non-period/non-delimiter characters, and basename is non-empty. +/////////////////////////////////////////////////////////////////////////////// + +class Pathname { +public: + // Folder delimiters are slash and backslash + static bool IsFolderDelimiter(char ch); + + Pathname(); + Pathname(const std::string& pathname); + + // Set's the default folder delimiter for this Pathname + char folder_delimiter() const { return folder_delimiter_; } + void SetFolderDelimiter(char delimiter); + + // Normalize changes all folder delimiters to folder_delimiter() + void Normalize(); + + // Reset to the empty pathname + void clear(); + + std::string url() const; + + std::string pathname() const; + void SetPathname(const std::string& pathname); + + // Append pathname to the current folder (if any). Any existing filename + // will be discarded. + void AppendPathname(const Pathname& pathname); + + std::string folder() const; + std::string folder_name() const; + std::string parent_folder() const; + // SetFolder and AppendFolder will append a folder delimiter, if needed. + void SetFolder(const std::string& folder); + void AppendFolder(const std::string& folder); + + std::string basename() const; + void SetBasename(const std::string& basename); + + std::string extension() const; + // SetExtension will prefix a period, if needed. + void SetExtension(const std::string& extension); + + std::string filename() const; + void SetFilename(const std::string& filename); + +private: + + std::string folder_, basename_, extension_; + char folder_delimiter_; +}; + + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_PATHUTILS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc new file mode 100644 index 0000000..9cdacdf --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.cc @@ -0,0 +1,1132 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <cassert> + +#ifdef POSIX +extern "C" { +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/time.h> +#include <unistd.h> +} +#endif + +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#define _WINSOCKAPI_ +#include <windows.h> +#undef SetPort +#endif + +#include <algorithm> +#include <iostream> + +#include "talk/base/basictypes.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/time.h" +#include "talk/base/winping.h" + +#ifdef __linux +#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h +#endif // __linux + +#ifdef WIN32 +class WinsockInitializer { +public: + WinsockInitializer() { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(1, 0); + err_ = WSAStartup(wVersionRequested, &wsaData); + } + ~WinsockInitializer() { + WSACleanup(); + } + int error() { + return err_; + } +private: + int err_; +}; +WinsockInitializer g_winsockinit; +#endif + +namespace talk_base { + +const int kfRead = 0x0001; +const int kfWrite = 0x0002; +const int kfConnect = 0x0004; +const int kfClose = 0x0008; + +// Standard MTUs +const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +const uint32 IP_HEADER_SIZE = 20; +const uint32 ICMP_HEADER_SIZE = 8; + +class PhysicalSocket : public AsyncSocket { +public: + PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET) + : ss_(ss), s_(s), enabled_events_(0), error_(0), + state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) { + if (s != INVALID_SOCKET) + enabled_events_ = kfRead | kfWrite; + } + + virtual ~PhysicalSocket() { + Close(); + } + + // Creates the underlying OS socket (same as the "socket" function). + virtual bool Create(int type) { + Close(); + s_ = ::socket(AF_INET, type, 0); + UpdateLastError(); + if (type != SOCK_STREAM) + enabled_events_ = kfRead | kfWrite; + return s_ != INVALID_SOCKET; + } + + SocketAddress GetLocalAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(s_, (sockaddr*)&addr, &addrlen); + ASSERT(addrlen == sizeof(addr)); + talk_base::SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } else { + ASSERT(result >= 0); + } + return address; + } + + SocketAddress GetRemoteAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(s_, (sockaddr*)&addr, &addrlen); + ASSERT(addrlen == sizeof(addr)); + talk_base::SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } else { + ASSERT(errno == ENOTCONN); + } + return address; + } + + int Bind(const SocketAddress& addr) { + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int err = ::bind(s_, (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return err; + } + + int Connect(const SocketAddress& addr) { + // TODO: Implicit creation is required to reconnect... + // ...but should we make it more explicit? + if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM)) + return SOCKET_ERROR; + SocketAddress addr2(addr); + if (addr2.IsUnresolved()) { + LOG(INFO) << "Resolving addr in PhysicalSocket::Connect"; + addr2.Resolve(); // TODO: Do this async later? + } + sockaddr_in saddr; + addr2.ToSockAddr(&saddr); + int err = ::connect(s_, (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_; + if (err == 0) { + state_ = CS_CONNECTED; + } else if (IsBlockingError(error_)) { + state_ = CS_CONNECTING; + enabled_events_ |= kfConnect; + } + enabled_events_ |= kfRead | kfWrite; + return err; + } + + int GetError() const { + return error_; + } + + void SetError(int error) { + error_ = error; + } + + ConnState GetState() const { + return state_; + } + + int SetOption(Option opt, int value) { + assert(opt == OPT_DONTFRAGMENT); +#ifdef WIN32 + value = (value == 0) ? 0 : 1; + return ::setsockopt( + s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast<char*>(&value), + sizeof(value)); +#endif +#ifdef __linux + value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO; + return ::setsockopt( + s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value)); +#endif +#ifdef OSX + // This is not possible on OSX. + return -1; +#endif + } + + int Send(const void *pv, size_t cb) { + int sent = ::send(s_, reinterpret_cast<const char *>(pv), (int)cb, 0); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_; + ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int sent = ::sendto( + s_, (const char *)pv, (int)cb, 0, (sockaddr*)&saddr, + sizeof(saddr)); + UpdateLastError(); + ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + if ((sent < 0) && IsBlockingError(error_)) { + enabled_events_ |= kfWrite; + } + return sent; + } + + int Recv(void *pv, size_t cb) { + int received = ::recv(s_, (char *)pv, (int)cb, 0); + if ((received == 0) && (cb != 0)) { + // Note: on graceful shutdown, recv can return 0. In this case, we + // pretend it is blocking, and then signal close, so that simplifying + // assumptions can be made about Recv. + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + UpdateLastError(); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr, + &cbAddr); + UpdateLastError(); + if ((received >= 0) && (paddr != NULL)) + paddr->FromSockAddr(saddr); + if ((received >= 0) || IsBlockingError(error_)) { + enabled_events_ |= kfRead; + } + return received; + } + + int Listen(int backlog) { + int err = ::listen(s_, backlog); + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + enabled_events_ |= kfRead; + + return err; + } + + Socket* Accept(SocketAddress *paddr) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + SOCKET s = ::accept(s_, (sockaddr*)&saddr, &cbAddr); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + if (paddr != NULL) + paddr->FromSockAddr(saddr); + enabled_events_ |= kfRead | kfWrite; + return ss_->WrapSocket(s); + } + + int Close() { + if (s_ == INVALID_SOCKET) + return 0; + int err = ::closesocket(s_); + UpdateLastError(); + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Close() Ret: " << err << " Error: " << error_; + s_ = INVALID_SOCKET; + state_ = CS_CLOSED; + enabled_events_ = 0; + return err; + } + + int EstimateMTU(uint16* mtu) { + SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + error_ = ENOTCONN; + return -1; + } + +#ifdef WIN32 + + WinPing ping; + if (!ping.IsValid()) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; + WinPing::PingResult result = ping.Ping(addr.ip(), size, 0, 1, false); + if (result == WinPing::PING_FAIL) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + if (result != WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + assert(false); + return 0; + +#endif // WIN32 + +#ifdef __linux + + int value; + socklen_t vlen = sizeof(value); + int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen); + if (err < 0) { + UpdateLastError(); + return err; + } + + assert((0 <= value) && (value <= 65536)); + *mtu = uint16(value); + return 0; + +#endif // __linux + + // TODO: OSX support + } + + SocketServer* socketserver() { return ss_; } + +protected: + PhysicalSocketServer* ss_; + SOCKET s_; + uint32 enabled_events_; + int error_; + ConnState state_; + + void UpdateLastError() { +#ifdef WIN32 + error_ = WSAGetLastError(); +#endif +#ifdef POSIX + error_ = errno; +#endif + } +}; + +#ifdef POSIX +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual int GetDescriptor() = 0; +}; + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { + if (pipe(afd_) < 0) + LOG(LERROR) << "pipe failed"; + ss_->Add(this); + } + + virtual ~EventDispatcher() { + ss_->Remove(this); + close(afd_[0]); + close(afd_[1]); + } + + virtual void Signal() { + CritScope cs(&crit_); + if (!fSignaled_) { + uint8 b = 0; + if (write(afd_[1], &b, sizeof(b)) < 0) + LOG(LERROR) << "write failed"; + fSignaled_ = true; + } + } + + virtual uint32 GetRequestedEvents() { + return kfRead; + } + + virtual void OnPreEvent(uint32 ff) { + // It is not possible to perfectly emulate an auto-resetting event with + // pipes. This simulates it by resetting before the event is handled. + + CritScope cs(&crit_); + if (fSignaled_) { + uint8 b; + read(afd_[0], &b, sizeof(b)); + fSignaled_ = false; + } + } + + virtual void OnEvent(uint32 ff, int err) { + assert(false); + } + + virtual int GetDescriptor() { + return afd_[0]; + } + +private: + PhysicalSocketServer *ss_; + int afd_[2]; + bool fSignaled_; + CriticalSection crit_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { + ss_->Add(this); + } + SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) { + ss_->Add(this); + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + ss_->Add(this); + fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK); + return true; + } + + virtual bool Create(int type) { + // Change the socket to be non-blocking. + if (!PhysicalSocket::Create(type)) + return false; + + return Initialize(); + } + + virtual int GetDescriptor() { + return s_; + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & kfConnect) != 0) + state_ = CS_CONNECTED; + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if ((ff & kfWrite) != 0) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if ((ff & kfConnect) != 0) { + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + ss_->Remove(this); + return PhysicalSocket::Close(); + } + +}; + +class FileDispatcher: public Dispatcher, public AsyncFile { +public: + FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) { + set_readable(true); + + ss_->Add(this); + + fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK); + } + + virtual ~FileDispatcher() { + ss_->Remove(this); + } + + SocketServer* socketserver() { return ss_; } + + virtual int GetDescriptor() { + return fd_; + } + + virtual uint32 GetRequestedEvents() { + return flags_; + } + + virtual void OnPreEvent(uint32 ff) { + } + + virtual void OnEvent(uint32 ff, int err) { + if ((ff & kfRead) != 0) + SignalReadEvent(this); + if ((ff & kfWrite) != 0) + SignalWriteEvent(this); + if ((ff & kfClose) != 0) + SignalCloseEvent(this, err); + } + + virtual bool readable() { + return (flags_ & kfRead) != 0; + } + + virtual void set_readable(bool value) { + flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead); + } + + virtual bool writable() { + return (flags_ & kfWrite) != 0; + } + + virtual void set_writable(bool value) { + flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite); + } + +private: + PhysicalSocketServer* ss_; + int fd_; + int flags_; +}; + +AsyncFile* PhysicalSocketServer::CreateFile(int fd) { + return new FileDispatcher(fd, this); +} + +#endif // POSIX + +#ifdef WIN32 +class Dispatcher { +public: + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +}; + +uint32 FlagsToEvents(uint32 events) { + uint32 ffFD = FD_CLOSE | FD_ACCEPT; + if (events & kfRead) + ffFD |= FD_READ; + if (events & kfWrite) + ffFD |= FD_WRITE; + if (events & kfConnect) + ffFD |= FD_CONNECT; + return ffFD; +} + +class EventDispatcher : public Dispatcher { +public: + EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) { + if (hev_ = WSACreateEvent()) { + ss_->Add(this); + } + } + + ~EventDispatcher() { + if (hev_ != NULL) { + ss_->Remove(this); + WSACloseEvent(hev_); + hev_ = NULL; + } + } + + virtual void Signal() { + if (hev_ != NULL) + WSASetEvent(hev_); + } + + virtual uint32 GetRequestedEvents() { + return 0; + } + + virtual void OnPreEvent(uint32 ff) { + WSAResetEvent(hev_); + } + + virtual void OnEvent(uint32 ff, int err) { + } + + virtual WSAEVENT GetWSAEvent() { + return hev_; + } + + virtual SOCKET GetSocket() { + return INVALID_SOCKET; + } + + virtual bool CheckSignalClose() { return false; } + +private: + PhysicalSocketServer* ss_; + WSAEVENT hev_; +}; + +class SocketDispatcher : public Dispatcher, public PhysicalSocket { +public: + static int next_id_; + int id_; + bool signal_close_; + int signal_err_; + + SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) { + } + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) { + } + + virtual ~SocketDispatcher() { + Close(); + } + + bool Initialize() { + assert(s_ != INVALID_SOCKET); + // Must be a non-blocking + u_long argp = 1; + ioctlsocket(s_, FIONBIO, &argp); + ss_->Add(this); + return true; + } + + virtual bool Create(int type) { + // Create socket + if (!PhysicalSocket::Create(type)) + return false; + + if (!Initialize()) + return false; + + do { id_ = ++next_id_; } while (id_ == 0); + return true; + } + + virtual int Close() { + if (s_ == INVALID_SOCKET) + return 0; + + id_ = 0; + signal_close_ = false; + ss_->Remove(this); + return PhysicalSocket::Close(); + } + + virtual uint32 GetRequestedEvents() { + return enabled_events_; + } + + virtual void OnPreEvent(uint32 ff) { + if ((ff & kfConnect) != 0) + state_ = CS_CONNECTED; + } + + virtual void OnEvent(uint32 ff, int err) { + int cache_id = id_; + if ((ff & kfRead) != 0) { + enabled_events_ &= ~kfRead; + SignalReadEvent(this); + } + if (((ff & kfWrite) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~kfWrite; + SignalWriteEvent(this); + } + if (((ff & kfConnect) != 0) && (id_ == cache_id)) { + if (ff != kfConnect) + LOG(LS_VERBOSE) << "Signalled with kfConnect: " << ff; + enabled_events_ &= ~kfConnect; + SignalConnectEvent(this); + } + if (((ff & kfClose) != 0) && (id_ == cache_id)) { + //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err; + signal_close_ = true; + signal_err_ = err; + } + } + + virtual WSAEVENT GetWSAEvent() { + return WSA_INVALID_EVENT; + } + + virtual SOCKET GetSocket() { + return s_; + } + + virtual bool CheckSignalClose() { + if (!signal_close_) + return false; + + char ch; + if (recv(s_, &ch, 1, MSG_PEEK) > 0) + return false; + + signal_close_ = false; + SignalCloseEvent(this, signal_err_); + return true; + } +}; + +int SocketDispatcher::next_id_ = 0; + +#endif // WIN32 + +// Sets the value of a boolean value to false when signaled. +class Signaler : public EventDispatcher { +public: + Signaler(PhysicalSocketServer* ss, bool* pf) + : EventDispatcher(ss), pf_(pf) { + } + virtual ~Signaler() { } + + void OnEvent(uint32 ff, int err) { + if (pf_) + *pf_ = false; + } + +private: + bool *pf_; +}; + +PhysicalSocketServer::PhysicalSocketServer() : fWait_(false), + last_tick_tracked_(0), last_tick_dispatch_count_(0) { + signal_wakeup_ = new Signaler(this, &fWait_); +} + +PhysicalSocketServer::~PhysicalSocketServer() { + delete signal_wakeup_; + // ASSERT(dispatchers_.empty()); +} + +void PhysicalSocketServer::WakeUp() { + signal_wakeup_->Signal(); +} + +Socket* PhysicalSocketServer::CreateSocket(int type) { + PhysicalSocket* socket = new PhysicalSocket(this); + if (socket->Create(type)) { + return socket; + } else { + delete socket; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) { + SocketDispatcher* dispatcher = new SocketDispatcher(this); + if (dispatcher->Create(type)) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) { + SocketDispatcher* dispatcher = new SocketDispatcher(s, this); + if (dispatcher->Initialize()) { + return dispatcher; + } else { + delete dispatcher; + return 0; + } +} + +void PhysicalSocketServer::Add(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.push_back(pdispatcher); +} + +void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) { + CritScope cs(&crit_); + dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end()); +} + +#ifdef POSIX +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { + // Calculate timing information + + struct timeval *ptvWait = NULL; + struct timeval tvWait; + struct timeval tvStop; + if (cmsWait != kForever) { + // Calculate wait timeval + tvWait.tv_sec = cmsWait / 1000; + tvWait.tv_usec = (cmsWait % 1000) * 1000; + ptvWait = &tvWait; + + // Calculate when to return in a timeval + gettimeofday(&tvStop, NULL); + tvStop.tv_sec += tvWait.tv_sec; + tvStop.tv_usec += tvWait.tv_usec; + if (tvStop.tv_usec >= 1000000) { + tvStop.tv_usec -= 1000000; + tvStop.tv_sec += 1; + } + } + + // Zero all fd_sets. Don't need to do this inside the loop since + // select() zeros the descriptors not signaled + + fd_set fdsRead; + FD_ZERO(&fdsRead); + fd_set fdsWrite; + FD_ZERO(&fdsWrite); + + fWait_ = true; + + while (fWait_) { + int fdmax = -1; + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + // Query dispatchers for read and write wait state + + Dispatcher *pdispatcher = dispatchers_[i]; + assert(pdispatcher); + if (!process_io && (pdispatcher != signal_wakeup_)) + continue; + int fd = pdispatcher->GetDescriptor(); + if (fd > fdmax) + fdmax = fd; + uint32 ff = pdispatcher->GetRequestedEvents(); + if (ff & kfRead) + FD_SET(fd, &fdsRead); + if (ff & (kfWrite | kfConnect)) + FD_SET(fd, &fdsWrite); + } + } + + // Wait then call handlers as appropriate + // < 0 means error + // 0 means timeout + // > 0 means count of descriptors ready + int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); + // If error, return error + // todo: do something intelligent + if (n < 0) + return false; + + // If timeout, return success + + if (n == 0) + return true; + + // We have signaled descriptors + + { + CritScope cr(&crit_); + for (unsigned i = 0; i < dispatchers_.size(); i++) { + Dispatcher *pdispatcher = dispatchers_[i]; + int fd = pdispatcher->GetDescriptor(); + uint32 ff = 0; + if (FD_ISSET(fd, &fdsRead)) { + FD_CLR(fd, &fdsRead); + ff |= kfRead; + } + if (FD_ISSET(fd, &fdsWrite)) { + FD_CLR(fd, &fdsWrite); + if (pdispatcher->GetRequestedEvents() & kfConnect) { + ff |= kfConnect; + } else { + ff |= kfWrite; + } + } + if (ff != 0) { + pdispatcher->OnPreEvent(ff); + pdispatcher->OnEvent(ff, 0); + } + } + } + + // Recalc the time remaining to wait. Doing it here means it doesn't get + // calced twice the first time through the loop + + if (cmsWait != kForever) { + ptvWait->tv_sec = 0; + ptvWait->tv_usec = 0; + struct timeval tvT; + gettimeofday(&tvT, NULL); + if (tvStop.tv_sec >= tvT.tv_sec) { + ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; + ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; + if (ptvWait->tv_usec < 0) { + ptvWait->tv_usec += 1000000; + ptvWait->tv_sec -= 1; + } + } + } + } + + return true; +} +#endif // POSIX + +#ifdef WIN32 +bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) +{ + int cmsTotal = cmsWait; + int cmsElapsed = 0; + uint32 msStart = GetMillisecondCount(); + +#if LOGGING + if (last_tick_dispatch_count_ == 0) { + last_tick_tracked_ = msStart; + } +#endif + + WSAEVENT socket_ev = WSACreateEvent(); + + fWait_ = true; + while (fWait_) { + std::vector<WSAEVENT> events; + std::vector<Dispatcher *> event_owners; + + events.push_back(socket_ev); + + { + CritScope cr(&crit_); + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + if (!process_io && (disp != signal_wakeup_)) + continue; + SOCKET s = disp->GetSocket(); + if (disp->CheckSignalClose()) { + // We just signalled close, don't poll this socket + } else if (s != INVALID_SOCKET) { + WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents())); + } else { + events.push_back(disp->GetWSAEvent()); + event_owners.push_back(disp); + } + } + } + + // Which is shorter, the delay wait or the asked wait? + + int cmsNext; + if (cmsWait == kForever) { + cmsNext = cmsWait; + } else { + cmsNext = cmsTotal - cmsElapsed; + if (cmsNext < 0) + cmsNext = 0; + } + + // Wait for one of the events to signal + DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), &events[0], false, cmsNext, false); + +#if 0 // LOGGING + // we track this information purely for logging purposes. + last_tick_dispatch_count_++; + if (last_tick_dispatch_count_ >= 1000) { + uint32 now = GetMillisecondCount(); + LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events"; + + // If we get more than 1000 events in a second, we are spinning badly + // (normally it should take about 8-20 seconds). + assert(TimeDiff(now, last_tick_tracked_) > 1000); + + last_tick_tracked_ = now; + last_tick_dispatch_count_ = 0; + } +#endif + + // Failed? + // todo: need a better strategy than this! + + if (dw == WSA_WAIT_FAILED) { + int error = WSAGetLastError(); + assert(false); + WSACloseEvent(socket_ev); + return false; + } + + // Timeout? + + if (dw == WSA_WAIT_TIMEOUT) { + WSACloseEvent(socket_ev); + return true; + } + + // Figure out which one it is and call it + + { + CritScope cr(&crit_); + int index = dw - WSA_WAIT_EVENT_0; + if (index > 0) { + --index; // The first event is the socket event + event_owners[index]->OnPreEvent(0); + event_owners[index]->OnEvent(0, 0); + } else if (process_io) { + for (size_t i = 0; i < dispatchers_.size(); ++i) { + Dispatcher * disp = dispatchers_[i]; + SOCKET s = disp->GetSocket(); + if (s == INVALID_SOCKET) + continue; + + WSANETWORKEVENTS wsaEvents; + int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents); + if (err == 0) { + +#if LOGGING + { + if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; + } + if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + } +#endif + uint32 ff = 0; + int errcode = 0; + if (wsaEvents.lNetworkEvents & FD_READ) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_WRITE) + ff |= kfWrite; + if (wsaEvents.lNetworkEvents & FD_CONNECT) { + if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { + ff |= kfConnect; + } else { + // TODO: Decide whether we want to signal connect, but with an error code + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; + } + } + if (wsaEvents.lNetworkEvents & FD_ACCEPT) + ff |= kfRead; + if (wsaEvents.lNetworkEvents & FD_CLOSE) { + ff |= kfClose; + errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; + } + if (ff != 0) { + disp->OnPreEvent(ff); + disp->OnEvent(ff, errcode); + } + } + } + } + + // Reset the network event until new activity occurs + WSAResetEvent(socket_ev); + } + + // Break? + + if (!fWait_) + break; + cmsElapsed = GetMillisecondCount() - msStart; + if ((cmsWait != kForever) && (cmsElapsed >= cmsWait)) { + break; + } + } + + // Done + + WSACloseEvent(socket_ev); + return true; +} +#endif // WIN32 + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h new file mode 100644 index 0000000..cc2e707 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/physicalsocketserver.h @@ -0,0 +1,81 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_PHYSICALSOCKETSERVER_H__ +#define TALK_BASE_PHYSICALSOCKETSERVER_H__ + +#include <vector> + +#include "talk/base/asyncfile.h" +#include "talk/base/socketserver.h" +#include "talk/base/criticalsection.h" + +#ifdef POSIX +typedef int SOCKET; +#endif // POSIX + +namespace talk_base { + +class Dispatcher; +class Signaler; + +// A socket server that provides the real sockets of the underlying OS. +class PhysicalSocketServer : public SocketServer { +public: + PhysicalSocketServer(); + virtual ~PhysicalSocketServer(); + + // SocketFactory: + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + + // Internal Factory for Accept + AsyncSocket* WrapSocket(SOCKET s); + + // SocketServer: + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + void Add(Dispatcher* dispatcher); + void Remove(Dispatcher* dispatcher); + +#ifdef POSIX + AsyncFile* CreateFile(int fd); +#endif + +private: + std::vector<Dispatcher*> dispatchers_; + Signaler* signal_wakeup_; + CriticalSection crit_; + bool fWait_; + uint32 last_tick_tracked_; + int last_tick_dispatch_count_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_PHYSICALSOCKETSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/proxydetect.cc b/Plugins/jingle/libjingle/talk/base/proxydetect.cc new file mode 100644 index 0000000..04a9c61 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/proxydetect.cc @@ -0,0 +1,827 @@ +// TODO: Abstract this better for cross-platformability + +#ifdef _WINDOWS +#include "talk/base/win32.h" +#include <shlobj.h> +#endif + +#include "talk/base/httpcommon.h" +#include "talk/base/httpcommon-inl.h" +#include "talk/base/proxydetect.h" +#include "talk/base/stringutils.h" +#include "talk/base/basicdefs.h" + +#if _WINDOWS +#define _TRY_FIREFOX 1 +#define _TRY_WINHTTP 1 +#define _TRY_JSPROXY 0 +#define _TRY_WM_FINDPROXY 0 +#define _TRY_IE_LAN_SETTINGS 1 +#endif // _WINDOWS + +#if _TRY_WINHTTP +//#include <winhttp.h> +// Note: From winhttp.h + +const char WINHTTP[] = "winhttp"; +typedef LPVOID HINTERNET; + +typedef struct { + DWORD dwAccessType; // see WINHTTP_ACCESS_* types below + LPWSTR lpszProxy; // proxy server list + LPWSTR lpszProxyBypass; // proxy bypass list +} WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO; + +typedef struct { + DWORD dwFlags; + DWORD dwAutoDetectFlags; + LPCWSTR lpszAutoConfigUrl; + LPVOID lpvReserved; + DWORD dwReserved; + BOOL fAutoLogonIfChallenged; +} WINHTTP_AUTOPROXY_OPTIONS; + +typedef struct { + BOOL fAutoDetect; + LPWSTR lpszAutoConfigUrl; + LPWSTR lpszProxy; + LPWSTR lpszProxyBypass; +} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; + +extern "C" { +typedef HINTERNET (WINAPI * pfnWinHttpOpen) +( + IN LPCWSTR pwszUserAgent, + IN DWORD dwAccessType, + IN LPCWSTR pwszProxyName OPTIONAL, + IN LPCWSTR pwszProxyBypass OPTIONAL, + IN DWORD dwFlags +); +typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle) +( + IN HINTERNET hInternet +); +typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl) +( + IN HINTERNET hSession, + IN LPCWSTR lpcwszUrl, + IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, + OUT WINHTTP_PROXY_INFO * pProxyInfo +); +typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig) +( + IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig +); + +} // extern "C" + +#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 +#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 +#define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000 +#define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000 +#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 +#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 +#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 +#define WINHTTP_ACCESS_TYPE_NO_PROXY 1 +#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 +#define WINHTTP_NO_PROXY_NAME NULL +#define WINHTTP_NO_PROXY_BYPASS NULL + +#endif // _TRY_WINHTTP + +#if _TRY_JSPROXY +extern "C" { +typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo) +( + LPCSTR lpszUrl, + DWORD dwUrlLength, + LPSTR lpszUrlHostName, + DWORD dwUrlHostNameLength, + LPSTR * lplpszProxyHostName, + LPDWORD lpdwProxyHostNameLength + ); +} // extern "C" +#endif // _TRY_JSPROXY + +#if _TRY_WM_FINDPROXY +#include <comutil.h> +#include <wmnetsourcecreator.h> +#include <wmsinternaladminnetsource.h> +#endif // _TRY_WM_FINDPROXY + +#if _TRY_IE_LAN_SETTINGS +#include <wininet.h> +#include <string> +#endif // _TRY_IE_LAN_SETTINGS + +using namespace talk_base; + +////////////////////////////////////////////////////////////////////// +// Utility Functions +////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS +#ifdef _UNICODE + +typedef std::wstring tstring; +std::string Utf8String(const tstring& str) { return ToUtf8(str); } + +#else // !_UNICODE + +typedef std::string tstring; +std::string Utf8String(const tstring& str) { return str; } + +#endif // !_UNICODE +#endif // _WINDOWS + +////////////////////////////////////////////////////////////////////// +// GetProxySettingsForUrl +////////////////////////////////////////////////////////////////////// + +bool WildMatch(const char * target, const char * pattern) { + while (*pattern) { + if (*pattern == '*') { + if (!*++pattern) { + return true; + } + while (*target) { + if ((toupper(*pattern) == toupper(*target)) && WildMatch(target + 1, pattern + 1)) { + return true; + } + ++target; + } + return false; + } else { + if (toupper(*pattern) != toupper(*target)) { + return false; + } + ++target; + ++pattern; + } + } + return !*target; +} + +bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) { + // hostname:443 + if (char * port = strchr(item, ':')) { + *port++ = '\0'; + if (url.port() != atol(port)) { + return false; + } + } + + // A.B.C.D or A.B.C.D/24 + int a, b, c, d, m; + int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m); + if (match >= 4) { + uint32 ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF); + if ((match < 5) || (m > 32)) + m = 32; + else if (m < 0) + m = 0; + uint32 mask = (m == 0) ? 0 : (~0UL) << (32 - m); + SocketAddress addr(url.server()); + return !addr.IsUnresolved() && ((addr.ip() & mask) == (ip & mask)); + } + + // .foo.com + if (*item == '.') { + size_t hostlen = url.server().length(); + return (hostlen > len) + && (stricmp(url.server().c_str() + (hostlen - len), item) == 0); + } + + // localhost or www.*.com + if (!WildMatch(url.server().c_str(), item)) + return false; + + return true; +} + +bool ProxyListMatch(const Url<char>& url, const std::string& slist, char sep) { + const size_t BUFSIZE = 256; + char buffer[BUFSIZE]; + const char* list = slist.c_str(); + while (*list) { + // Remove leading space + if (isspace(*list)) { + ++list; + continue; + } + // Break on separator + size_t len; + const char * start = list; + if (const char * end = strchr(list, sep)) { + len = (end - list); + list += len + 1; + } else { + len = strlen(list); + list += len; + } + // Remove trailing space + while ((len > 0) && isspace(start[len-1])) + --len; + // Check for oversized entry + if (len >= BUFSIZE) + continue; + memcpy(buffer, start, len); + buffer[len] = 0; + if (!ProxyItemMatch(url, buffer, len)) + continue; + return true; + } + return false; +} + +bool Better(ProxyType lhs, const ProxyType rhs) { + // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN + const int PROXY_VALUE[4] = { 0, 2, 3, 1 }; + return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]); +} + +bool ParseProxy(const std::string& saddress, ProxyInfo& proxy) { + const size_t kMaxAddressLength = 1024; + // Allow semicolon, space, or tab as an address separator + const char* const kAddressSeparator = " ;\t"; + + ProxyType ptype; + std::string host; + uint16 port; + + const char* address = saddress.c_str(); + while (*address) { + size_t len; + const char * start = address; + if (const char * sep = strchr(address, kAddressSeparator)) { + len = (sep - address); + address += len + 1; + while (strchr(kAddressSeparator, *address)) { + address += 1; + } + } else { + len = strlen(address); + address += len; + } + + if (len > kMaxAddressLength - 1) { + LOG(LS_WARNING) << "Proxy address too long [" << start << "]"; + continue; + } + + char buffer[kMaxAddressLength]; + memcpy(buffer, start, len); + buffer[len] = 0; + + char * colon = strchr(buffer, ':'); + if (!colon) { + LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]"; + continue; + } + + *colon = 0; + char * endptr; + port = static_cast<uint16>(strtol(colon + 1, &endptr, 0)); + if (*endptr != 0) { + LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]"; + continue; + } + + if (char * equals = strchr(buffer, '=')) { + *equals = 0; + host = equals + 1; + if (_stricmp(buffer, "socks") == 0) { + ptype = PROXY_SOCKS5; + } else if (_stricmp(buffer, "https") == 0) { + ptype = PROXY_HTTPS; + } else { + LOG(LS_WARNING) << "Proxy address with unknown protocol [" + << buffer << "]"; + ptype = PROXY_UNKNOWN; + } + } else { + host = buffer; + ptype = PROXY_UNKNOWN; + } + + if (Better(ptype, proxy.type)) { + proxy.type = ptype; + proxy.address.SetIP(host); + proxy.address.SetPort((int)port); + } + } + + return (proxy.type != PROXY_NONE); +} + +#if _WINDOWS +bool IsDefaultBrowserFirefox() { + HKEY key; + LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command", + 0, KEY_READ, &key); + if (ERROR_SUCCESS != result) + return false; + + wchar_t* value = NULL; + DWORD size, type; + result = RegQueryValueEx(key, L"", 0, &type, NULL, &size); + if (REG_SZ != type) { + result = ERROR_ACCESS_DENIED; // Any error is fine + } else if (ERROR_SUCCESS == result) { + value = new wchar_t[size+1]; + BYTE* buffer = reinterpret_cast<BYTE*>(value); + result = RegQueryValueEx(key, L"", 0, &type, buffer, &size); + } + RegCloseKey(key); + + bool success = false; + if (ERROR_SUCCESS == result) { + value[size] = L'\0'; + for (size_t i=0; i<size; ++i) { + value[i] = tolowercase(value[i]); + } + success = (NULL != strstr(value, L"firefox.exe")); + } + delete [] value; + return success; +} +#endif + +#if _TRY_FIREFOX + +#define USE_FIREFOX_PROFILES_INI 1 + +bool GetDefaultFirefoxProfile(std::wstring* profile) { + ASSERT(NULL != profile); + + wchar_t path[MAX_PATH]; + if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, path) != S_OK) + return false; + + std::wstring profile_root(path); + profile_root.append(L"\\Mozilla\\Firefox\\"); + +#if USE_FIREFOX_PROFILES_INI + std::wstring tmp(profile_root); + tmp.append(L"profiles.ini"); + + FILE * fp = _wfopen(tmp.c_str(), L"rb"); + if (!fp) + return false; + + // [Profile0] + // Name=default + // IsRelative=1 + // Path=Profiles/2de53ejb.default + // Default=1 + + // Note: we are looking for the first entry with "Default=1", or the last entry in the file + + std::wstring candidate; + bool relative = true; + + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), fp)) { + size_t len = strlen(buffer); + while ((len > 0) && isspace(buffer[len-1])) + buffer[--len] = 0; + if (buffer[0] == '[') { + relative = true; + candidate.clear(); + } else if (strnicmp(buffer, "IsRelative=", 11) == 0) { + relative = (buffer[11] != '0'); + } else if (strnicmp(buffer, "Path=", 5) == 0) { + if (relative) { + candidate = profile_root; + } else { + candidate.clear(); + } + candidate.append(ToUtf16(buffer + 5)); + candidate.append(L"\\"); + } else if (strnicmp(buffer, "Default=", 8) == 0) { + if ((buffer[8] != '0') && !candidate.empty()) { + break; + } + } + } + fclose(fp); + if (candidate.empty()) + return false; + *profile = candidate; + +#else // !USE_FIREFOX_PROFILES_INI + std::wstring tmp(profile_root); + tmp.append(L"Profiles\\*.default"); + WIN32_FIND_DATA fdata; + HANDLE hFind = FindFirstFile(tmp.c_str(), &fdata); + if (hFind == INVALID_HANDLE_VALUE) + return false; + + profile->assign(profile_root); + profile->append(L"Profiles\\"); + profile->append(fdata.cFileName); + profile->append(L"\\"); + FindClose(hFind); +#endif // !USE_FIREFOX_PROFILES_INI + + return true; +} + +struct StringMap { +public: + void Add(const char * name, const char * value) { map_[name] = value; } + const std::string& Get(const char * name, const char * def = "") const { + std::map<std::string, std::string>::const_iterator it = + map_.find(name); + if (it != map_.end()) + return it->second; + def_ = def; + return def_; + } + bool IsSet(const char * name) const { + return (map_.find(name) != map_.end()); + } +private: + std::map<std::string, std::string> map_; + mutable std::string def_; +}; + +bool ReadFirefoxPrefs(const std::wstring& filename, + const char * prefix, + StringMap& settings) { + FILE * fp = _wfopen(filename.c_str(), L"rb"); + if (!fp) + return false; + + size_t prefix_len = strlen(prefix); + bool overlong_line = false; + + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), fp)) { + size_t len = strlen(buffer); + bool missing_newline = (len > 0) && (buffer[len-1] != '\n'); + + if (missing_newline) { + overlong_line = true; + continue; + } else if (overlong_line) { + LOG_F(LS_INFO) << "Skipping long line"; + overlong_line = false; + continue; + } + + while ((len > 0) && isspace(buffer[len-1])) + buffer[--len] = 0; + + // Skip blank lines + if ((len == 0) || (buffer[0] == '#') + || (strncmp(buffer, "/*", 2) == 0) + || (strncmp(buffer, " *", 2) == 0)) + continue; + + int nstart = 0, nend = 0, vstart = 0, vend = 0; + sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);", + &nstart, &nend, &vstart, &vend); + if (vend > 0) { + char * name = buffer + nstart; + name[nend - nstart] = 0; + if ((vend - vstart >= 2) && (buffer[vstart] == '"')) { + vstart += 1; + vend -= 1; + } + char * value = buffer + vstart; + value[vend - vstart] = 0; + if ((strncmp(name, prefix, prefix_len) == 0) && *value) { + settings.Add(name + prefix_len, value); + } + } else { + LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]"; + } + } + fclose(fp); + return true; +} +#endif // _TRY_FIREFOX + +#ifdef WIN32 +BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU, + HINTERNET hWinHttp, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options, + WINHTTP_PROXY_INFO *info) { + // WinHttpGetProxyForUrl() can call plugins which can crash. + // In the case of McAfee scriptproxy.dll, it does crash in + // older versions. Try to catch crashes here and treat as an + // error. + BOOL success = FALSE; + +#if (_HAS_EXCEPTIONS == 0) + __try { + success = pWHGPFU(hWinHttp, url, options, info); + } __except(EXCEPTION_EXECUTE_HANDLER) { + LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetProxyForUrl faulted!!"; + } +#else + success = pWHGPFU(hWinHttp, url, options, info); +#endif + + return success; +} +#endif + +bool GetProxySettingsForUrl(const char* agent, const char* url, + ProxyInfo& proxy, + bool long_operation) { + bool success = false; + Url<char> purl(url); + +#if 0 + assert( WildMatch(_T("A.B.C.D"), _T("a.b.c.d"))); + assert( WildMatch(_T("127.0.0.1"), _T("12*.0.*1"))); + assert(!WildMatch(_T("127.0.0.0"), _T("12*.0.*1"))); + assert(!WildMatch(_T("127.0.0.0"), _T("12*.0.*1"))); + assert( WildMatch(_T("127.1.0.21"), _T("12*.0.*1"))); + assert(!WildMatch(_T("127.1.1.21"), _T("12*.0.*1"))); + purl = PUrl(_T("http://a.b.c:500/")); + wchar_t item[256]; + _tcscpy(item, _T("a.b.c")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("a.x.c")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("a.b.*")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("a.x.*")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T(".b.c")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T(".x.c")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("a.b.c:500")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("a.b.c:501")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); + purl = PUrl(_T("http://1.2.3.4/")); + _tcscpy(item, _T("1.2.3.4")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("1.2.3.5")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("1.2.3.5/31")); + assert( ProxyItemMatch(purl, item, _tcslen(item))); + _tcscpy(item, _T("1.2.3.5/32")); + assert(!ProxyItemMatch(purl, item, _tcslen(item))); +#endif + + bool autoconfig = false; + bool use_firefox = false; + std::string autoconfig_url; + +#if _TRY_FIREFOX + use_firefox = IsDefaultBrowserFirefox(); + + if (use_firefox) { + std::wstring tmp; + if (GetDefaultFirefoxProfile(&tmp)) { + bool complete = true; + + StringMap settings; + tmp.append(L"prefs.js"); + if (ReadFirefoxPrefs(tmp, "network.proxy.", settings)) { + success = true; + if (settings.Get("type") == "1") { + if (ProxyListMatch(purl, settings.Get("no_proxies_on", "localhost, 127.0.0.1").c_str(), ',')) { + // Bypass proxy + } else if (settings.Get("share_proxy_settings") == "true") { + proxy.type = PROXY_UNKNOWN; + proxy.address.SetIP(settings.Get("http")); + proxy.address.SetPort(atoi(settings.Get("http_port").c_str())); + } else if (settings.IsSet("socks")) { + proxy.type = PROXY_SOCKS5; + proxy.address.SetIP(settings.Get("socks")); + proxy.address.SetPort(atoi(settings.Get("socks_port").c_str())); + } else if (settings.IsSet("ssl")) { + proxy.type = PROXY_HTTPS; + proxy.address.SetIP(settings.Get("ssl")); + proxy.address.SetPort(atoi(settings.Get("ssl_port").c_str())); + } else if (settings.IsSet("http")) { + proxy.type = PROXY_HTTPS; + proxy.address.SetIP(settings.Get("http")); + proxy.address.SetPort(atoi(settings.Get("http_port").c_str())); + } + } else if (settings.Get("type") == "2") { + complete = success = false; + autoconfig_url = settings.Get("autoconfig_url").c_str(); + } else if (settings.Get("type") == "4") { + complete = success = false; + autoconfig = true; + } + } + if (complete) { // Otherwise fall through to IE autoproxy code + return success; + } + } + } +#endif // _TRY_FIREFOX + +#if _TRY_WINHTTP + if (!success) { + if (HMODULE hModWH = LoadLibrary(L"winhttp.dll")) { + pfnWinHttpOpen pWHO = reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(hModWH, "WinHttpOpen")); + pfnWinHttpCloseHandle pWHCH = reinterpret_cast<pfnWinHttpCloseHandle>(GetProcAddress(hModWH, "WinHttpCloseHandle")); + pfnWinHttpGetProxyForUrl pWHGPFU = reinterpret_cast<pfnWinHttpGetProxyForUrl>(GetProcAddress(hModWH, "WinHttpGetProxyForUrl")); + pfnWinHttpGetIEProxyConfig pWHGIEPC = reinterpret_cast<pfnWinHttpGetIEProxyConfig>(GetProcAddress(hModWH, "WinHttpGetIEProxyConfigForCurrentUser")); + if (pWHO && pWHCH && pWHGPFU && pWHGIEPC) { + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg; + memset(&iecfg, 0, sizeof(iecfg)); + if (!use_firefox && !pWHGIEPC(&iecfg)) { + LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetIEProxyConfigForCurrentUser"; + } else { + success = true; + if (!use_firefox) { + if (iecfg.fAutoDetect) { + autoconfig = true; + } + if (iecfg.lpszAutoConfigUrl) { + autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl); + } + } + if (!long_operation) { + // Unless we perform this operation in the background, don't allow + // it to take a long time. + autoconfig = false; + } + if (autoconfig || !autoconfig_url.empty()) { + if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(), + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0)) { + WINHTTP_AUTOPROXY_OPTIONS options; + memset(&options, 0, sizeof(options)); + if (autoconfig) { + options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP + | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + std::wstring autoconfig_url16((ToUtf16)(autoconfig_url)); + if (!autoconfig_url.empty()) { + options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + options.lpszAutoConfigUrl = autoconfig_url16.c_str(); + } + options.fAutoLogonIfChallenged = TRUE; + WINHTTP_PROXY_INFO info; + memset(&info, 0, sizeof(info)); + + BOOL success = MyWinHttpGetProxyForUrl(pWHGPFU, + hWinHttp, ToUtf16(url).c_str(), &options, &info); + + if (!success) { + LOG_GLEM(LERROR,WINHTTP) << "WinHttpGetProxyForUrl"; + } else { + if (iecfg.lpszProxy) + GlobalFree(iecfg.lpszProxy); + if (iecfg.lpszProxyBypass) + GlobalFree(iecfg.lpszProxyBypass); + iecfg.lpszProxy = info.lpszProxy; + iecfg.lpszProxyBypass = info.lpszProxyBypass; + } + pWHCH(hWinHttp); + } + } + if (!ProxyListMatch(purl, ToUtf8(nonnull(iecfg.lpszProxyBypass)), ' ')) { + ParseProxy(ToUtf8(nonnull(iecfg.lpszProxy)), proxy); + } + if (iecfg.lpszAutoConfigUrl) + GlobalFree(iecfg.lpszAutoConfigUrl); + if (iecfg.lpszProxy) + GlobalFree(iecfg.lpszProxy); + if (iecfg.lpszProxyBypass) + GlobalFree(iecfg.lpszProxyBypass); + } + } + FreeLibrary(hModWH); + } + } +#endif // _TRY_WINHTTP + +#if _TRY_JSPROXY + if (!success) { + if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) { + pfnInternetGetProxyInfo pIGPI = reinterpret_cast<pfnInternetGetProxyInfo>(GetProcAddress(hModJS, "InternetGetProxyInfo")); + if (pIGPI) { + char proxy[256], host[256]; + memset(proxy, 0, sizeof(proxy)); + char * ptr = proxy; + DWORD proxylen = sizeof(proxy); + std::string surl = Utf8String(url); + DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S", purl.secure() ? "s" : "", purl.server()); + if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) { + LOG(INFO) << "Proxy: " << proxy; + } else { + LOG_GLE(INFO) << "InternetGetProxyInfo"; + } + } + FreeLibrary(hModJS); + } + } +#endif // _TRY_JSPROXY + +#if _TRY_WM_FINDPROXY + if (!success) { + INSNetSourceCreator * nsc = 0; + HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL, IID_INSNetSourceCreator, (LPVOID *) &nsc); + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr = nsc->Initialize())) { + VARIANT dispatch; + VariantInit(&dispatch); + if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) { + IWMSInternalAdminNetSource * ians = 0; + if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface(IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) { + _bstr_t host(purl.server()); + BSTR proxy = 0; + BOOL bProxyEnabled = FALSE; + DWORD port, context = 0; + if (SUCCEEDED(hr = ians->FindProxyForURL(L"http", host, &bProxyEnabled, &proxy, &port, &context))) { + success = true; + if (bProxyEnabled) { + _bstr_t sproxy = proxy; + proxy.ptype = PT_HTTPS; + proxy.host = sproxy; + proxy.port = port; + } + } + SysFreeString(proxy); + if (FAILED(hr = ians->ShutdownProxyContext(context))) { + LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext failed: " << hr; + } + ians->Release(); + } + } + VariantClear(&dispatch); + if (FAILED(hr = nsc->Shutdown())) { + LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr; + } + } + nsc->Release(); + } + } +#endif // _TRY_WM_FINDPROXY + +#if _TRY_IE_LAN_SETTINGS + if (!success) { + wchar_t buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer); + DWORD dwSize = sizeof(buffer); + + if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) { + LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); + } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) { + success = true; + } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) { + success = true; + if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>(info->lpszProxyBypass)), ' ')) { + ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)), proxy); + } + } else { + LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType; + } + } +#endif // _TRY_IE_LAN_SETTINGS + +#if 0 + if (!success) { + INTERNET_PER_CONN_OPTION_LIST list; + INTERNET_PER_CONN_OPTION options[3]; + memset(&list, 0, sizeof(list)); + memset(&options, 0, sizeof(options)); + + list.dwSize = sizeof(list); + list.dwOptionCount = 3; + list.pOptions = options; + options[0].dwOption = INTERNET_PER_CONN_FLAGS; + options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER; + options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; + DWORD dwSize = sizeof(list); + + if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, &dwSize)) { + LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); + } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) { + success = true; + if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) { + ParseProxy(nonnull(options[1].Value.pszValue), proxy); + } + } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) { + success = true; + } else { + LOG(LS_INFO) << "unknown internet access type: " + << options[0].Value.dwValue; + } + if (options[1].Value.pszValue) { + GlobalFree(options[1].Value.pszValue); + } + if (options[2].Value.pszValue) { + GlobalFree(options[2].Value.pszValue); + } + } +#endif // 0 + + return success; +} diff --git a/Plugins/jingle/libjingle/talk/base/proxydetect.h b/Plugins/jingle/libjingle/talk/base/proxydetect.h new file mode 100644 index 0000000..3de2598 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/proxydetect.h @@ -0,0 +1,13 @@ +#ifndef _PROXYDETECT_H_ +#define _PROXYDETECT_H_ + +#include "talk/base/proxyinfo.h" + +// Auto-detect the proxy server. Returns true if a proxy is configured, +// although hostname may be empty if the proxy is not required for the given URL. + +bool GetProxySettingsForUrl(const char* agent, const char* url, + talk_base::ProxyInfo& proxy, + bool long_operation = false); + +#endif // _PROXYDETECT_H_ diff --git a/Plugins/jingle/libjingle/talk/base/proxyinfo.cc b/Plugins/jingle/libjingle/talk/base/proxyinfo.cc new file mode 100644 index 0000000..1d9c588 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/proxyinfo.cc @@ -0,0 +1,37 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/proxyinfo.h" + +namespace talk_base { + +const char * ProxyToString(ProxyType proxy) { + const char * const PROXY_NAMES[] = { "none", "https", "socks5", "unknown" }; + return PROXY_NAMES[proxy]; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/proxyinfo.h b/Plugins/jingle/libjingle/talk/base/proxyinfo.h new file mode 100644 index 0000000..834ec4f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/proxyinfo.h @@ -0,0 +1,51 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_PROXYINFO_H__ +#define TALK_BASE_PROXYINFO_H__ + +#include <string> +#include "talk/base/socketaddress.h" +#include "talk/base/cryptstring.h" + +namespace talk_base { + +enum ProxyType { PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN }; +const char * ProxyToString(ProxyType proxy); + +struct ProxyInfo { + ProxyType type; + SocketAddress address; + std::string username; + CryptString password; + + ProxyInfo() : type(PROXY_NONE) { } +}; + +} // namespace talk_base + +#endif // TALK_BASE_PROXYINFO_H__ diff --git a/Plugins/jingle/libjingle/talk/base/schanneladapter.cc b/Plugins/jingle/libjingle/talk/base/schanneladapter.cc new file mode 100644 index 0000000..d971c7e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/schanneladapter.cc @@ -0,0 +1,749 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/win32.h" +#define SECURITY_WIN32 +#include <security.h> +#include <schannel.h> + +#include <iomanip> +#include <vector> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/schanneladapter.h" +#include "talk/base/sec_buffer.h" +#include "talk/base/thread.h" + +namespace talk_base { + +///////////////////////////////////////////////////////////////////////////// +// SChannelAdapter +///////////////////////////////////////////////////////////////////////////// + +extern const ConstantLabel SECURITY_ERRORS[]; + +const ConstantLabel SECURITY_ERRORS[] = { + KLABEL(SEC_I_COMPLETE_AND_CONTINUE), + KLABEL(SEC_I_COMPLETE_NEEDED), + KLABEL(SEC_I_CONTEXT_EXPIRED), + KLABEL(SEC_I_CONTINUE_NEEDED), + KLABEL(SEC_I_INCOMPLETE_CREDENTIALS), + KLABEL(SEC_I_RENEGOTIATE), + KLABEL(SEC_E_CERT_EXPIRED), + KLABEL(SEC_E_INCOMPLETE_MESSAGE), + KLABEL(SEC_E_INSUFFICIENT_MEMORY), + KLABEL(SEC_E_INTERNAL_ERROR), + KLABEL(SEC_E_INVALID_HANDLE), + KLABEL(SEC_E_INVALID_TOKEN), + KLABEL(SEC_E_LOGON_DENIED), + KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY), + KLABEL(SEC_E_NO_CREDENTIALS), + KLABEL(SEC_E_NOT_OWNER), + KLABEL(SEC_E_OK), + KLABEL(SEC_E_SECPKG_NOT_FOUND), + KLABEL(SEC_E_TARGET_UNKNOWN), + KLABEL(SEC_E_UNKNOWN_CREDENTIALS), + KLABEL(SEC_E_UNSUPPORTED_FUNCTION), + KLABEL(SEC_E_UNTRUSTED_ROOT), + KLABEL(SEC_E_WRONG_PRINCIPAL), + LASTLABEL +}; + +const ConstantLabel SCHANNEL_BUFFER_TYPES[] = { + KLABEL(SECBUFFER_EMPTY), // 0 + KLABEL(SECBUFFER_DATA), // 1 + KLABEL(SECBUFFER_TOKEN), // 2 + KLABEL(SECBUFFER_PKG_PARAMS), // 3 + KLABEL(SECBUFFER_MISSING), // 4 + KLABEL(SECBUFFER_EXTRA), // 5 + KLABEL(SECBUFFER_STREAM_TRAILER), // 6 + KLABEL(SECBUFFER_STREAM_HEADER), // 7 + KLABEL(SECBUFFER_MECHLIST), // 11 + KLABEL(SECBUFFER_MECHLIST_SIGNATURE), // 12 + KLABEL(SECBUFFER_TARGET), // 13 + KLABEL(SECBUFFER_CHANNEL_BINDINGS), // 14 + LASTLABEL +}; + +void DescribeBuffer(LoggingSeverity severity, const char* prefix, + const SecBuffer& sb) { + LOG_V(severity) + << prefix + << "(" << sb.cbBuffer + << ", " << FindLabel(sb.BufferType & ~SECBUFFER_ATTRMASK, + SCHANNEL_BUFFER_TYPES) + << ", " << sb.pvBuffer << ")"; +} + +void DescribeBuffers(LoggingSeverity severity, const char* prefix, + const SecBufferDesc* sbd) { + if (!LOG_CHECK_LEVEL_V(severity)) + return; + LOG_V(severity) << prefix << "("; + for (size_t i=0; i<sbd->cBuffers; ++i) { + DescribeBuffer(severity, " ", sbd->pBuffers[i]); + } + LOG_V(severity) << ")"; +} + +const ULONG SSL_FLAGS_DEFAULT = ISC_REQ_ALLOCATE_MEMORY + | ISC_REQ_CONFIDENTIALITY + | ISC_REQ_EXTENDED_ERROR + | ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + | ISC_REQ_STREAM; + //| ISC_REQ_USE_SUPPLIED_CREDS; + +typedef std::vector<char> SChannelBuffer; + +struct SChannelAdapter::SSLImpl { + CredHandle cred; + CtxtHandle ctx; + bool cred_init, ctx_init; + SChannelBuffer inbuf, outbuf, readable; + SecPkgContext_StreamSizes sizes; + + SSLImpl() : cred_init(false), ctx_init(false) { } +}; + +SChannelAdapter::SChannelAdapter(AsyncSocket* socket) + : SSLAdapter(socket), state_(SSL_NONE), + restartable_(false), signal_close_(false), message_pending_(false), + impl_(new SSLImpl) { +} + +SChannelAdapter::~SChannelAdapter() { + Cleanup(); +} + +int +SChannelAdapter::StartSSL(const char* hostname, bool restartable) { + if (state_ != SSL_NONE) + return ERROR_ALREADY_INITIALIZED; + + ssl_host_name_ = hostname; + restartable_ = restartable; + + if (socket_->GetState() != Socket::CS_CONNECTED) { + state_ = SSL_WAIT; + return 0; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err, false); + return err; + } + + return 0; +} + +int +SChannelAdapter::BeginSSL() { + LOG(LS_VERBOSE) << "BeginSSL: " << ssl_host_name_; + ASSERT(state_ == SSL_CONNECTING); + + SECURITY_STATUS ret; + + SCHANNEL_CRED sc_cred = { 0 }; + sc_cred.dwVersion = SCHANNEL_CRED_VERSION; + //sc_cred.dwMinimumCipherStrength = 128; // Note: use system default + sc_cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_AUTO_CRED_VALIDATION; + + ret = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, + &sc_cred, NULL, NULL, &impl_->cred, NULL); + if (ret != SEC_E_OK) { + LOG(LS_ERROR) << "AcquireCredentialsHandle error: " + << ErrorName(ret, SECURITY_ERRORS); + return ret; + } + impl_->cred_init = true; + + if (LOG_CHECK_LEVEL(LS_VERBOSE)) { + SecPkgCred_CipherStrengths cipher_strengths = { 0 }; + ret = QueryCredentialsAttributes(&impl_->cred, + SECPKG_ATTR_CIPHER_STRENGTHS, + &cipher_strengths); + if (SUCCEEDED(ret)) { + LOG(LS_VERBOSE) << "SChannel cipher strength: " + << cipher_strengths.dwMinimumCipherStrength << " - " + << cipher_strengths.dwMaximumCipherStrength; + } + + SecPkgCred_SupportedAlgs supported_algs = { 0 }; + ret = QueryCredentialsAttributes(&impl_->cred, + SECPKG_ATTR_SUPPORTED_ALGS, + &supported_algs); + if (SUCCEEDED(ret)) { + LOG(LS_VERBOSE) << "SChannel supported algorithms:"; + for (DWORD i=0; i<supported_algs.cSupportedAlgs; ++i) { + ALG_ID alg_id = supported_algs.palgSupportedAlgs[i]; + PCCRYPT_OID_INFO oinfo = CryptFindOIDInfo(CRYPT_OID_INFO_ALGID_KEY, + &alg_id, 0); + LPCWSTR alg_name = (NULL != oinfo) ? oinfo->pwszName : L"Unknown"; + LOG(LS_VERBOSE) << " " << talk_base::ToUtf8(alg_name) + << " (" << alg_id << ")"; + } + } + } + + ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0; + if (ignore_bad_cert()) + flags |= ISC_REQ_MANUAL_CRED_VALIDATION; + + CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out; + ret = InitializeSecurityContextA(&impl_->cred, NULL, + const_cast<char*>(ssl_host_name_.c_str()), + flags, 0, 0, NULL, 0, + &impl_->ctx, sb_out.desc(), + &ret_flags, NULL); + if (SUCCEEDED(ret)) + impl_->ctx_init = true; + return ProcessContext(ret, NULL, sb_out.desc()); +} + +int +SChannelAdapter::ContinueSSL() { + LOG(LS_VERBOSE) << "ContinueSSL"; + ASSERT(state_ == SSL_CONNECTING); + + SECURITY_STATUS ret; + + CSecBufferBundle<2> sb_in; + sb_in[0].BufferType = SECBUFFER_TOKEN; + sb_in[0].cbBuffer = static_cast<unsigned long>(impl_->inbuf.size()); + sb_in[0].pvBuffer = &impl_->inbuf[0]; + //DescribeBuffers(LS_VERBOSE, "Input Buffer ", sb_in.desc()); + + ULONG flags = SSL_FLAGS_DEFAULT, ret_flags = 0; + if (ignore_bad_cert()) + flags |= ISC_REQ_MANUAL_CRED_VALIDATION; + + CSecBufferBundle<2, CSecBufferBase::FreeSSPI> sb_out; + ret = InitializeSecurityContextA(&impl_->cred, &impl_->ctx, + const_cast<char*>(ssl_host_name_.c_str()), + flags, 0, 0, sb_in.desc(), 0, + NULL, sb_out.desc(), + &ret_flags, NULL); + return ProcessContext(ret, sb_in.desc(), sb_out.desc()); +} + +int +SChannelAdapter::ProcessContext(long int status, _SecBufferDesc* sbd_in, + _SecBufferDesc* sbd_out) { + LoggingSeverity level = LS_ERROR; + if ((status == SEC_E_OK) + || (status != SEC_I_CONTINUE_NEEDED) + || (status != SEC_E_INCOMPLETE_MESSAGE)) { + level = LS_VERBOSE; // Expected messages + } + LOG_V(level) + << "InitializeSecurityContext error: " + << ErrorName(status, SECURITY_ERRORS); + //if (sbd_in) + // DescribeBuffers(LS_VERBOSE, "Input Buffer ", sbd_in); + //if (sbd_out) + // DescribeBuffers(LS_VERBOSE, "Output Buffer ", sbd_out); + + if (status == SEC_E_INCOMPLETE_MESSAGE) { + // Wait for more input from server. + return Flush(); + } + + if (FAILED(status)) { + // We can't continue. Common errors: + // SEC_E_CERT_EXPIRED - Typically, this means the computer clock is wrong. + return status; + } + + // Note: we check both input and output buffers for SECBUFFER_EXTRA. + // Experience shows it appearing in the input, but the documentation claims + // it should appear in the output. + size_t extra = 0; + if (sbd_in) { + for (size_t i=0; i<sbd_in->cBuffers; ++i) { + SecBuffer& buffer = sbd_in->pBuffers[i]; + if (buffer.BufferType == SECBUFFER_EXTRA) { + extra += buffer.cbBuffer; + } + } + } + if (sbd_out) { + for (size_t i=0; i<sbd_out->cBuffers; ++i) { + SecBuffer& buffer = sbd_out->pBuffers[i]; + if (buffer.BufferType == SECBUFFER_EXTRA) { + extra += buffer.cbBuffer; + } else if (buffer.BufferType == SECBUFFER_TOKEN) { + impl_->outbuf.insert(impl_->outbuf.end(), + reinterpret_cast<char*>(buffer.pvBuffer), + reinterpret_cast<char*>(buffer.pvBuffer) + buffer.cbBuffer); + } + } + } + + if (extra) { + ASSERT(extra <= impl_->inbuf.size()); + size_t consumed = impl_->inbuf.size() - extra; + memmove(&impl_->inbuf[0], &impl_->inbuf[consumed], extra); + impl_->inbuf.resize(extra); + } else { + impl_->inbuf.clear(); + } + + if (SEC_I_CONTINUE_NEEDED == status) { + // Send data to server and wait for response. + // Note: ContinueSSL will result in a Flush, anyway. + return impl_->inbuf.empty() ? Flush() : ContinueSSL(); + } + + if (SEC_E_OK == status) { + LOG(LS_VERBOSE) << "QueryContextAttributes"; + status = QueryContextAttributes(&impl_->ctx, SECPKG_ATTR_STREAM_SIZES, + &impl_->sizes); + if (FAILED(status)) { + LOG(LS_ERROR) << "QueryContextAttributes error: " + << ErrorName(status, SECURITY_ERRORS); + return status; + } + + state_ = SSL_CONNECTED; + + if (int err = DecryptData()) { + return err; + } else if (int err = Flush()) { + return err; + } else { + // If we decrypted any data, queue up a notification here + PostEvent(); + // Signal our connectedness + AsyncSocketAdapter::OnConnectEvent(this); + } + return 0; + } + + if (SEC_I_INCOMPLETE_CREDENTIALS == status) { + // We don't support client authentication in schannel. + return status; + } + + // We don't expect any other codes + ASSERT(false); + return status; +} + +int +SChannelAdapter::DecryptData() { + SChannelBuffer& inbuf = impl_->inbuf; + SChannelBuffer& readable = impl_->readable; + + while (!inbuf.empty()) { + CSecBufferBundle<4> in_buf; + in_buf[0].BufferType = SECBUFFER_DATA; + in_buf[0].cbBuffer = static_cast<unsigned long>(inbuf.size()); + in_buf[0].pvBuffer = &inbuf[0]; + + //DescribeBuffers(LS_VERBOSE, "Decrypt In ", in_buf.desc()); + SECURITY_STATUS status = DecryptMessage(&impl_->ctx, in_buf.desc(), 0, 0); + //DescribeBuffers(LS_VERBOSE, "Decrypt Out ", in_buf.desc()); + + // Note: We are explicitly treating SEC_E_OK, SEC_I_CONTEXT_EXPIRED, and + // any other successful results as continue. + if (SUCCEEDED(status)) { + size_t data_len = 0, extra_len = 0; + for (size_t i=0; i<in_buf.desc()->cBuffers; ++i) { + if (in_buf[i].BufferType == SECBUFFER_DATA) { + data_len += in_buf[i].cbBuffer; + readable.insert(readable.end(), + reinterpret_cast<char*>(in_buf[i].pvBuffer), + reinterpret_cast<char*>(in_buf[i].pvBuffer) + in_buf[i].cbBuffer); + } else if (in_buf[i].BufferType == SECBUFFER_EXTRA) { + extra_len += in_buf[i].cbBuffer; + } + } + // There is a bug on Win2K where SEC_I_CONTEXT_EXPIRED is misclassified. + if ((data_len == 0) && (inbuf[0] == 0x15)) { + status = SEC_I_CONTEXT_EXPIRED; + } + if (extra_len) { + size_t consumed = inbuf.size() - extra_len; + memmove(&inbuf[0], &inbuf[consumed], extra_len); + inbuf.resize(extra_len); + } else { + inbuf.clear(); + } + // TODO: Handle SEC_I_CONTEXT_EXPIRED to do clean shutdown + if (status != SEC_E_OK) { + LOG(LS_INFO) << "DecryptMessage returned continuation code: " + << ErrorName(status, SECURITY_ERRORS); + } + continue; + } + + if (status == SEC_E_INCOMPLETE_MESSAGE) { + break; + } else { + return status; + } + } + + return 0; +} + +void +SChannelAdapter::Cleanup() { + if (impl_->ctx_init) + DeleteSecurityContext(&impl_->ctx); + if (impl_->cred_init) + FreeCredentialsHandle(&impl_->cred); + delete impl_; +} + +void +SChannelAdapter::PostEvent() { + // Check if there's anything notable to signal + if (impl_->readable.empty() && !signal_close_) + return; + + // Only one post in the queue at a time + if (message_pending_) + return; + + if (Thread* thread = Thread::Current()) { + message_pending_ = true; + thread->Post(this); + } else { + LOG(LS_ERROR) << "No thread context available for SChannelAdapter"; + ASSERT(false); + } +} + +void +SChannelAdapter::Error(const char* context, int err, bool signal) { + LOG(LS_WARNING) << "SChannelAdapter::Error(" + << context << ", " + << ErrorName(err, SECURITY_ERRORS) << ")"; + state_ = SSL_ERROR; + SetError(err); + if (signal) + AsyncSocketAdapter::OnCloseEvent(this, err); +} + +int +SChannelAdapter::Read() { + char buffer[4096]; + SChannelBuffer& inbuf = impl_->inbuf; + while (true) { + int ret = AsyncSocketAdapter::Recv(buffer, sizeof(buffer)); + if (ret > 0) { + inbuf.insert(inbuf.end(), buffer, buffer + ret); + } else if (GetError() == EWOULDBLOCK) { + return 0; // Blocking + } else { + return GetError(); + } + } +} + +int +SChannelAdapter::Flush() { + int result = 0; + size_t pos = 0; + SChannelBuffer& outbuf = impl_->outbuf; + while (pos < outbuf.size()) { + int sent = AsyncSocketAdapter::Send(&outbuf[pos], outbuf.size() - pos); + if (sent > 0) { + pos += sent; + } else if (GetError() == EWOULDBLOCK) { + break; // Blocking + } else { + result = GetError(); + break; + } + } + if (int remainder = outbuf.size() - pos) { + memmove(&outbuf[0], &outbuf[pos], remainder); + outbuf.resize(remainder); + } else { + outbuf.clear(); + } + return result; +} + +// +// AsyncSocket Implementation +// + +int +SChannelAdapter::Send(const void* pv, size_t cb) { + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Send(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + size_t written = 0; + SChannelBuffer& outbuf = impl_->outbuf; + while (written < cb) { + const size_t encrypt_len = std::min<size_t>(cb - written, + impl_->sizes.cbMaximumMessage); + + CSecBufferBundle<4> out_buf; + out_buf[0].BufferType = SECBUFFER_STREAM_HEADER; + out_buf[0].cbBuffer = impl_->sizes.cbHeader; + out_buf[1].BufferType = SECBUFFER_DATA; + out_buf[1].cbBuffer = static_cast<unsigned long>(encrypt_len); + out_buf[2].BufferType = SECBUFFER_STREAM_TRAILER; + out_buf[2].cbBuffer = impl_->sizes.cbTrailer; + + size_t packet_len = out_buf[0].cbBuffer + + out_buf[1].cbBuffer + + out_buf[2].cbBuffer; + + SChannelBuffer message; + message.resize(packet_len); + out_buf[0].pvBuffer = &message[0]; + out_buf[1].pvBuffer = &message[out_buf[0].cbBuffer]; + out_buf[2].pvBuffer = &message[out_buf[0].cbBuffer + out_buf[1].cbBuffer]; + + memcpy(out_buf[1].pvBuffer, + static_cast<const char*>(pv) + written, + encrypt_len); + + //DescribeBuffers(LS_VERBOSE, "Encrypt In ", out_buf.desc()); + SECURITY_STATUS res = EncryptMessage(&impl_->ctx, 0, out_buf.desc(), 0); + //DescribeBuffers(LS_VERBOSE, "Encrypt Out ", out_buf.desc()); + + if (FAILED(res)) { + Error("EncryptMessage", res, false); + return SOCKET_ERROR; + } + + // We assume that the header and data segments do not change length, + // or else encrypting the concatenated packet in-place is wrong. + ASSERT(out_buf[0].cbBuffer == impl_->sizes.cbHeader); + ASSERT(out_buf[1].cbBuffer == static_cast<unsigned long>(encrypt_len)); + + // However, the length of the trailer may change due to padding. + ASSERT(out_buf[2].cbBuffer <= impl_->sizes.cbTrailer); + + packet_len = out_buf[0].cbBuffer + + out_buf[1].cbBuffer + + out_buf[2].cbBuffer; + + written += encrypt_len; + outbuf.insert(outbuf.end(), &message[0], &message[packet_len-1]+1); + } + + if (int err = Flush()) { + state_ = SSL_ERROR; + SetError(err); + return SOCKET_ERROR; + } + + return static_cast<int>(written); +} + +int +SChannelAdapter::Recv(void* pv, size_t cb) { + switch (state_) { + case SSL_NONE: + return AsyncSocketAdapter::Recv(pv, cb); + + case SSL_WAIT: + case SSL_CONNECTING: + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + + case SSL_CONNECTED: + break; + + case SSL_ERROR: + default: + return SOCKET_ERROR; + } + + SChannelBuffer& readable = impl_->readable; + if (readable.empty()) { + SetError(EWOULDBLOCK); + return SOCKET_ERROR; + } + size_t read = min(cb, readable.size()); + memcpy(pv, &readable[0], read); + if (size_t remaining = readable.size() - read) { + memmove(&readable[0], &readable[read], remaining); + readable.resize(remaining); + } else { + readable.clear(); + } + + PostEvent(); + return static_cast<int>(read); +} + +int +SChannelAdapter::Close() { + if (!impl_->readable.empty()) { + LOG(WARNING) << "SChannelAdapter::Close with readable data"; + // Note: this isn't strictly an error, but we're using it temporarily to + // track bugs. + //ASSERT(false); + } + if (state_ == SSL_CONNECTED) { + DWORD token = SCHANNEL_SHUTDOWN; + CSecBufferBundle<1> sb_in; + sb_in[0].BufferType = SECBUFFER_TOKEN; + sb_in[0].cbBuffer = sizeof(token); + sb_in[0].pvBuffer = &token; + ApplyControlToken(&impl_->ctx, sb_in.desc()); + // TODO: In theory, to do a nice shutdown, we need to begin shutdown + // negotiation with more calls to InitializeSecurityContext. Since the + // socket api doesn't support nice shutdown at this point, we don't bother. + } + Cleanup(); + impl_ = new SSLImpl; + state_ = restartable_ ? SSL_WAIT : SSL_NONE; + signal_close_ = false; + message_pending_ = false; + return AsyncSocketAdapter::Close(); +} + +Socket::ConnState +SChannelAdapter::GetState() const { + if (signal_close_) + return CS_CONNECTED; + ConnState state = socket_->GetState(); + if ((state == CS_CONNECTED) + && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING))) + state = CS_CONNECTING; + return state; +} + +void +SChannelAdapter::OnConnectEvent(AsyncSocket* socket) { + LOG(LS_VERBOSE) << "SChannelAdapter::OnConnectEvent"; + if (state_ != SSL_WAIT) { + ASSERT(state_ == SSL_NONE); + AsyncSocketAdapter::OnConnectEvent(socket); + return; + } + + state_ = SSL_CONNECTING; + if (int err = BeginSSL()) { + Error("BeginSSL", err); + } +} + +void +SChannelAdapter::OnReadEvent(AsyncSocket* socket) { + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (int err = Read()) { + Error("Read", err); + return; + } + + if (impl_->inbuf.empty()) + return; + + if (state_ == SSL_CONNECTED) { + if (int err = DecryptData()) { + Error("DecryptData", err); + } else if (!impl_->readable.empty()) { + AsyncSocketAdapter::OnReadEvent(this); + } + } else if (state_ == SSL_CONNECTING) { + if (int err = ContinueSSL()) { + Error("ContinueSSL", err); + } + } +} + +void +SChannelAdapter::OnWriteEvent(AsyncSocket* socket) { + if (state_ == SSL_NONE) { + AsyncSocketAdapter::OnWriteEvent(socket); + return; + } + + if (int err = Flush()) { + Error("Flush", err); + return; + } + + // See if we have more data to write + if (!impl_->outbuf.empty()) + return; + + // Buffer is empty, submit notification + if (state_ == SSL_CONNECTED) { + AsyncSocketAdapter::OnWriteEvent(socket); + } +} + +void +SChannelAdapter::OnCloseEvent(AsyncSocket* socket, int err) { + if ((state_ == SSL_NONE) || impl_->readable.empty()) { + AsyncSocketAdapter::OnCloseEvent(socket, err); + return; + } + + // If readable is non-empty, then we have a pending Message + // that will allow us to signal close (eventually). + signal_close_ = true; +} + +void +SChannelAdapter::OnMessage(Message* pmsg) { + if (!message_pending_) + return; // This occurs when socket is closed + + message_pending_ = false; + if (!impl_->readable.empty()) { + AsyncSocketAdapter::OnReadEvent(this); + } else if (signal_close_) { + signal_close_ = false; + AsyncSocketAdapter::OnCloseEvent(this, 0); // TODO: cache this error? + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/schanneladapter.h b/Plugins/jingle/libjingle/talk/base/schanneladapter.h new file mode 100644 index 0000000..a5ab7b3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/schanneladapter.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SCHANNELADAPTER_H__ +#define TALK_BASE_SCHANNELADAPTER_H__ + +#include <string> +#include "talk/base/ssladapter.h" +#include "talk/base/messagequeue.h" +struct _SecBufferDesc; + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +class SChannelAdapter : public SSLAdapter, public MessageHandler { +public: + SChannelAdapter(AsyncSocket* socket); + virtual ~SChannelAdapter(); + + virtual int StartSSL(const char* hostname, bool restartable); + virtual int Send(const void* pv, size_t cb); + virtual int Recv(void* pv, size_t cb); + virtual int Close(); + + // Note that the socket returns ST_CONNECTING while SSL is being negotiated. + virtual ConnState GetState() const; + +protected: + enum SSLState { + SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR + }; + struct SSLImpl; + + virtual void OnConnectEvent(AsyncSocket* socket); + virtual void OnReadEvent(AsyncSocket* socket); + virtual void OnWriteEvent(AsyncSocket* socket); + virtual void OnCloseEvent(AsyncSocket* socket, int err); + virtual void OnMessage(Message* pmsg); + + int BeginSSL(); + int ContinueSSL(); + int ProcessContext(long int status, _SecBufferDesc* sbd_in, + _SecBufferDesc* sbd_out); + int DecryptData(); + + int Read(); + int Flush(); + void Error(const char* context, int err, bool signal = true); + void Cleanup(); + + void PostEvent(); + +private: + SSLState state_; + std::string ssl_host_name_; + // If true, socket will retain SSL configuration after Close. + bool restartable_; + // If true, we are delaying signalling close until all data is read. + bool signal_close_; + // If true, we are waiting to be woken up to signal readability or closure. + bool message_pending_; + SSLImpl* impl_; +}; + +///////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_SCHANNELADAPTER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/scoped_ptr.h b/Plugins/jingle/libjingle/talk/base/scoped_ptr.h new file mode 100644 index 0000000..c5e3c5d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/scoped_ptr.h @@ -0,0 +1,259 @@ +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. +// Copyright (c) 2001, 2002 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation. +// + +// scoped_ptr mimics a built-in pointer except that it guarantees deletion +// of the object pointed to, either on destruction of the scoped_ptr or via +// an explicit reset(). scoped_ptr is a simple solution for simple needs; +// use shared_ptr or std::auto_ptr if your needs are more complex. + +// scoped_ptr_malloc added in by Google. When one of +// these goes out of scope, instead of doing a delete or delete[], it +// calls free(). scoped_ptr_malloc<char> is likely to see much more +// use than any other specializations. + +// release() added in by Google. Use this to conditionally +// transfer ownership of a heap-allocated object to the caller, usually on +// method success. +#ifndef TALK_BASE_SCOPED_PTR_H__ +#define TALK_BASE_SCOPED_PTR_H__ + +#include <cstddef> // for std::ptrdiff_t +#include <assert.h> // for assert +#include <stdlib.h> // for free() decl + +#ifdef _WIN32 +namespace std { using ::ptrdiff_t; }; +#endif // _WIN32 + +namespace talk_base { + +template <typename T> +class scoped_ptr { + private: + + T* ptr; + + scoped_ptr(scoped_ptr const &); + scoped_ptr & operator=(scoped_ptr const &); + + public: + + typedef T element_type; + + explicit scoped_ptr(T* p = 0): ptr(p) {} + + ~scoped_ptr() { + typedef char type_must_be_complete[sizeof(T)]; + delete ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete ptr; + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + delete ptr; + ptr = 0; + } + return &ptr; + } + + T** use() { + return &ptr; + } +}; + +template<typename T> inline +void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) { + a.swap(b); +} + + + + +// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to +// is guaranteed, either on destruction of the scoped_array or via an explicit +// reset(). Use shared_array or std::vector if your needs are more complex. + +template<typename T> +class scoped_array { + private: + + T* ptr; + + scoped_array(scoped_array const &); + scoped_array & operator=(scoped_array const &); + + public: + + typedef T element_type; + + explicit scoped_array(T* p = 0) : ptr(p) {} + + ~scoped_array() { + typedef char type_must_be_complete[sizeof(T)]; + delete[] ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete [] ptr; + ptr = p; + } + } + + T& operator[](std::ptrdiff_t i) const { + assert(ptr != 0); + assert(i >= 0); + return ptr[i]; + } + + T* get() const { + return ptr; + } + + void swap(scoped_array & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + delete [] ptr; + ptr = 0; + } + return &ptr; + } +}; + +template<class T> inline +void swap(scoped_array<T>& a, scoped_array<T>& b) { + a.swap(b); +} + +// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a +// second template argument, the function used to free the object. + +template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc { + private: + + T* ptr; + + scoped_ptr_malloc(scoped_ptr_malloc const &); + scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); + + public: + + typedef T element_type; + + explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} + + ~scoped_ptr_malloc() { + typedef char type_must_be_complete[sizeof(T)]; + FF(static_cast<void*>(ptr)); + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + FF(static_cast<void*>(ptr)); + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr_malloc & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + T** accept() { + if (ptr) { + FF(static_cast<void*>(ptr)); + ptr = 0; + } + return &ptr; + } +}; + +template<typename T, void (*FF)(void*)> inline +void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) { + a.swap(b); +} + +} // namespace talk_base + +// TODO: get rid of this global using +using talk_base::scoped_ptr; + +#endif // #ifndef TALK_BASE_SCOPED_PTR_H__ diff --git a/Plugins/jingle/libjingle/talk/base/sec_buffer.h b/Plugins/jingle/libjingle/talk/base/sec_buffer.h new file mode 100644 index 0000000..585e27f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/sec_buffer.h @@ -0,0 +1,173 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// @file Contains utility classes that make it easier to use SecBuffers + +#ifndef TALK_BASE_SEC_BUFFER_H__ +#define TALK_BASE_SEC_BUFFER_H__ + +namespace talk_base { + +// A base class for CSecBuffer<T>. Contains +// all implementation that does not require +// template arguments. +class CSecBufferBase : public SecBuffer { + public: + CSecBufferBase() { + Clear(); + } + + // Uses the SSPI to free a pointer, must be + // used for buffers returned from SSPI APIs. + static void FreeSSPI(void *ptr) { + if ( ptr ) { + SECURITY_STATUS status; + status = ::FreeContextBuffer(ptr); + ASSERT(SEC_E_OK == status); // "Freeing context buffer" + } + } + + // Deletes a buffer with operator delete + static void FreeDelete(void *ptr) { + delete [] reinterpret_cast<char*>(ptr); + } + + // A noop delete, for buffers over other + // people's memory + static void FreeNone(void *ptr) { + } + + protected: + // Clears the buffer to EMPTY & NULL + void Clear() { + this->BufferType = SECBUFFER_EMPTY; + this->cbBuffer = 0; + this->pvBuffer = NULL; + } +}; + +// Wrapper class for SecBuffer to take care +// of initialization and destruction. +template <void (*pfnFreeBuffer)(void *ptr)> +class CSecBuffer: public CSecBufferBase { + public: + // Initializes buffer to empty & NULL + CSecBuffer() { + } + + // Frees any allocated memory + ~CSecBuffer() { + Release(); + } + + // Frees the buffer appropriately, and re-nulls + void Release() { + pfnFreeBuffer(this->pvBuffer); + Clear(); + } + + private: + // A placeholder function for compile-time asserts on the class + void CompileAsserts() { + // never invoked... + assert(false); // _T("Notreached") + + // This class must not extend the size of SecBuffer, since + // we use arrays of CSecBuffer in CSecBufferBundle below + cassert(sizeof(CSecBuffer<SSPIFree> == sizeof(SecBuffer))); + } +}; + +// Contains all generic implementation for the +// SecBufferBundle class +class SecBufferBundleBase { + public: +}; + +// A template class that bundles a SecBufferDesc with +// one or more SecBuffers for convenience. Can take +// care of deallocating buffers appropriately, as indicated +// by pfnFreeBuffer function. +// By default does no deallocation. +template <int num_buffers, + void (*pfnFreeBuffer)(void *ptr) = CSecBufferBase::FreeNone> +class CSecBufferBundle : public SecBufferBundleBase { + public: + // Constructs a security buffer bundle with num_buffers + // buffers, all of which are empty and nulled. + CSecBufferBundle() { + desc_.ulVersion = SECBUFFER_VERSION; + desc_.cBuffers = num_buffers; + desc_.pBuffers = buffers_; + } + + // Frees all currently used buffers. + ~CSecBufferBundle() { + Release(); + } + + // Accessor for the descriptor + PSecBufferDesc desc() { + return &desc_; + } + + // Accessor for the descriptor + const PSecBufferDesc desc() const { + return &desc_; + } + + // returns the i-th security buffer + SecBuffer &operator[] (size_t num) { + ASSERT(num < num_buffers); // "Buffer index out of bounds" + return buffers_[num]; + } + + // returns the i-th security buffer + const SecBuffer &operator[] (size_t num) const { + ASSERT(num < num_buffers); // "Buffer index out of bounds" + return buffers_[num]; + } + + // Frees all non-NULL security buffers, + // using the deallocation function + void Release() { + for ( size_t i = 0; i < num_buffers; ++i ) { + buffers_[i].Release(); + } + } + + private: + // Our descriptor + SecBufferDesc desc_; + // Our bundled buffers, each takes care of its own + // initialization and destruction + CSecBuffer<pfnFreeBuffer> buffers_[num_buffers]; +}; + +} // namespace talk_base + +#endif // TALK_BASE_SEC_BUFFER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/signalthread.cc b/Plugins/jingle/libjingle/talk/base/signalthread.cc new file mode 100644 index 0000000..919d1c3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/signalthread.cc @@ -0,0 +1,93 @@ +#include "talk/base/common.h" +#include "talk/base/signalthread.h" + +using namespace talk_base; + +/////////////////////////////////////////////////////////////////////////////// +// SignalThread +/////////////////////////////////////////////////////////////////////////////// + +SignalThread::SignalThread() +: main_(Thread::Current()), state_(kInit) +{ + worker_.parent_ = this; +} + +SignalThread::~SignalThread() { +} + +void SignalThread::SetPriority(ThreadPriority priority) { + ASSERT(main_->IsCurrent()); + ASSERT(kInit == state_); + worker_.SetPriority(priority); +} + +void SignalThread::Start() { + ASSERT(main_->IsCurrent()); + if (kInit == state_) { + state_ = kRunning; + OnWorkStart(); + worker_.Start(); + } else { + ASSERT(false); + } +} + +void SignalThread::Destroy() { + ASSERT(main_->IsCurrent()); + if ((kInit == state_) || (kComplete == state_)) { + delete this; + } else if (kRunning == state_) { + state_ = kStopping; + // A couple tricky issues here: + // 1) Thread::Stop() calls Join(), which we don't want... we just want + // to stop the MessageQueue, which causes ContinueWork() to return false. + // 2) OnWorkStop() must follow Stop(), so that when the thread wakes up + // due to OWS(), ContinueWork() will return false. + worker_.MessageQueue::Stop(); + OnWorkStop(); + } else { + ASSERT(false); + } +} + +void SignalThread::Release() { + ASSERT(main_->IsCurrent()); + if (kComplete == state_) { + delete this; + } else if (kRunning == state_) { + state_ = kReleasing; + } else { + // if (kInit == state_) use Destroy() + ASSERT(false); + } +} + +bool SignalThread::ContinueWork() { + ASSERT(worker_.IsCurrent()); + return worker_.ProcessMessages(0); +} + +void SignalThread::OnMessage(Message *msg) { + if (ST_MSG_WORKER_DONE == msg->message_id) { + ASSERT(main_->IsCurrent()); + OnWorkDone(); + bool do_delete = false; + if (kRunning == state_) { + state_ = kComplete; + } else { + do_delete = true; + } + if (kStopping != state_) { + SignalWorkDone(this); + } + if (do_delete) { + delete this; + } + } +} + +void SignalThread::Run() { + DoWork(); + main_->Post(this, ST_MSG_WORKER_DONE); +} diff --git a/Plugins/jingle/libjingle/talk/base/signalthread.h b/Plugins/jingle/libjingle/talk/base/signalthread.h new file mode 100644 index 0000000..9e6bd05 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/signalthread.h @@ -0,0 +1,91 @@ +#ifndef _SIGNALTHREAD_H_ +#define _SIGNALTHREAD_H_ + +#include "talk/base/thread.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// SignalThread - Base class for worker threads. The main thread should call +// Start() to begin work, and then follow one of these models: +// Normal: Wait for SignalWorkDone, and then call Release to destroy. +// Cancellation: Call Release(true), to abort the worker thread. +// Fire-and-forget: Call Release(false), which allows the thread to run to +// completion, and then self-destruct without further notification. +// The subclass should override DoWork() to perform the background task. By +// periodically calling ContinueWork(), it can check for cancellation. +// OnWorkStart and OnWorkDone can be overridden to do pre- or post-work +// tasks in the context of the main thread. +/////////////////////////////////////////////////////////////////////////////// + +class SignalThread : protected MessageHandler { +public: + SignalThread(); + + // Context: Main Thread. Call before Start to change the worker's priority. + void SetPriority(ThreadPriority priority); + + // Context: Main Thread. Call to begin the worker thread. + void Start(); + + // Context: Main Thread. If the worker thread is not running, deletes the + // object immediately. Otherwise, asks the worker thread to abort processing, + // and schedules the object to be deleted once the worker exits. + // SignalWorkDone will not be signalled. + void Destroy(); + + // Context: Main Thread. If the worker thread is complete, deletes the + // object immediately. Otherwise, schedules the object to be deleted once + // the worker thread completes. SignalWorkDone will be signalled. + void Release(); + + // Context: Main Thread. Signalled when work is complete. + sigslot::signal1<SignalThread *> SignalWorkDone; + + enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE }; + +protected: + virtual ~SignalThread(); + + // Context: Main Thread. Subclass should override to do pre-work setup. + virtual void OnWorkStart() { } + + // Context: Worker Thread. Subclass should override to do work. + virtual void DoWork() = 0; + + // Context: Worker Thread. Subclass should call periodically to + // dispatch messages and determine if the thread should terminate. + bool ContinueWork(); + + // Context: Worker Thread. Subclass should override when extra work is + // needed to abort the worker thread. + virtual void OnWorkStop() { } + + // Context: Main Thread. Subclass should override to do post-work cleanup. + virtual void OnWorkDone() { } + + // Context: Any Thread. If subclass overrides, be sure to call the base + // implementation. Do not use (message_id < ST_MSG_FIRST_AVAILABLE) + virtual void OnMessage(Message *msg); + +private: + friend class Worker; + class Worker : public Thread { + public: + SignalThread* parent_; + virtual void Run() { parent_->Run(); } + }; + + void Run(); + + Thread* main_; + Worker worker_; + enum State { kInit, kRunning, kComplete, kStopping, kReleasing } state_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // _SIGNALTHREAD_H_ diff --git a/Plugins/jingle/libjingle/talk/base/sigslot.h b/Plugins/jingle/libjingle/talk/base/sigslot.h new file mode 100644 index 0000000..539db8a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/sigslot.h @@ -0,0 +1,2699 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef TALK_BASE_SIGSLOT_H__ +#define TALK_BASE_SIGSLOT_H__ + +#include <set> +#include <list> + +// On our copy of sigslot.h, we force single threading +#define SIGSLOT_PURE_ISO + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include <windows.h> +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include <pthread.h> +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +// TODO: change this namespace to talk_base? +namespace sigslot { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + + template<class mt_policy> + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + template<class mt_policy> + class has_slots; + + template<class mt_policy> + class _connection_base0 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class mt_policy> + class _connection_base1 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1<arg1_type, mt_policy>* clone() = 0; + virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class mt_policy> + class _connection_base2 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0; + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _connection_base3 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0; + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy> + class _connection_base4 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0; + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy> + class _connection_base5 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* clone() = 0; + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy> + class _connection_base6 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* clone() = 0; + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _connection_base7 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0; + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy> + class _connection_base8 + { + public: + virtual has_slots<mt_policy>* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0; + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0; + }; + + template<class mt_policy> + class _signal_base : public mt_policy + { + public: + virtual void slot_disconnect(has_slots<mt_policy>* pslot) = 0; + virtual void slot_duplicate(const has_slots<mt_policy>* poldslot, has_slots<mt_policy>* pnewslot) = 0; + }; + + template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class has_slots : public mt_policy + { + private: + typedef typename std::set<_signal_base<mt_policy> *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block<mt_policy> lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base<mt_policy>* sender) + { + lock_block<mt_policy> lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base<mt_policy>* sender) + { + lock_block<mt_policy> lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template<class mt_policy> + class _signal_base0 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base0<mt_policy> *> connections_list; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class mt_policy> + class _signal_base1 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base1<arg1_type, mt_policy> *> connections_list; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1<arg1_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class mt_policy> + class _signal_base2 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *> + connections_list; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _signal_base3 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *> + connections_list; + + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy> + class _signal_base4 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy> *> connections_list; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy> + class _signal_base5 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, mt_policy> *> connections_list; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy> + class _signal_base6 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy> *> connections_list; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _signal_base7 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *> connections_list; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy> + class _signal_base8 : public _signal_base<mt_policy> + { + public: + typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *> + connections_list; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s) + : _signal_base<mt_policy>(s) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = s.m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + +#ifdef _DEBUG + bool connected(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots<mt_policy>* pclass) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots<mt_policy>* pslot) + { + lock_block<mt_policy> lock(this); + typename connections_list::iterator it = m_connected_slots.begin(); + typename connections_list::iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + typename connections_list::iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template<class dest_type, class mt_policy> + class _connection0 : public _connection_base0<mt_policy> + { + public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base0<mt_policy>* clone() + { + return new _connection0<dest_type, mt_policy>(*this); + } + + virtual _connection_base0<mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template<class dest_type, class arg1_type, class mt_policy> + class _connection1 : public _connection_base1<arg1_type, mt_policy> + { + public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base1<arg1_type, mt_policy>* clone() + { + return new _connection1<dest_type, arg1_type, mt_policy>(*this); + } + + virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class mt_policy> + class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy> + { + public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() + { + return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this); + } + + virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy> + class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> + { + public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() + { + return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this); + } + + virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class mt_policy> + class _connection4 : public _connection_base4<arg1_type, arg2_type, + arg3_type, arg4_type, mt_policy> + { + public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() + { + return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this); + } + + virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class mt_policy> + class _connection5 : public _connection_base5<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, mt_policy> + { + public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* clone() + { + return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>(*this); + } + + virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class mt_policy> + class _connection6 : public _connection_base6<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> + { + public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* clone() + { + return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>(*this); + } + + virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy> + class _connection7 : public _connection_base7<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> + { + public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* clone() + { + return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>(*this); + } + + virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template<class dest_type, class arg1_type, class arg2_type, class arg3_type, + class arg4_type, class arg5_type, class arg6_type, class arg7_type, + class arg8_type, class mt_policy> + class _connection8 : public _connection_base8<arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> + { + public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() + { + return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this); + } + + virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) + { + return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots<mt_policy>* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal0 : public _signal_base0<mt_policy> + { + public: + typedef _signal_base0<mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal0() + { + ; + } + + signal0(const signal0<mt_policy>& s) + : _signal_base0<mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block<mt_policy> lock(this); + _connection0<desttype, mt_policy>* conn = + new _connection0<desttype, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal1 : public _signal_base1<arg1_type, mt_policy> + { + public: + typedef _signal_base1<arg1_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal1() + { + ; + } + + signal1(const signal1<arg1_type, mt_policy>& s) + : _signal_base1<arg1_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block<mt_policy> lock(this); + _connection1<desttype, arg1_type, mt_policy>* conn = + new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy> + { + public: + typedef _signal_base2<arg1_type, arg2_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal2() + { + ; + } + + signal2(const signal2<arg1_type, arg2_type, mt_policy>& s) + : _signal_base2<arg1_type, arg2_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block<mt_policy> lock(this); + _connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new + _connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> + { + public: + typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal3() + { + ; + } + + signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s) + : _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block<mt_policy> lock(this); + _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn = + new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy> + { + public: + typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal4() + { + ; + } + + signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s) + : _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block<mt_policy> lock(this); + _connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* + conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, mt_policy> + { + public: + typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal5() + { + ; + } + + signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>& s) + : _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block<mt_policy> lock(this); + _connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type, + arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy> + { + public: + typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal6() + { + ; + } + + signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>& s) + : _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block<mt_policy> lock(this); + _connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, mt_policy>* conn = + new _connection6<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> + { + public: + typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal7() + { + ; + } + + signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>& s) + : _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block<mt_policy> lock(this); + _connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, mt_policy>* conn = + new _connection7<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, + class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> + class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> + { + public: + typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base; + typedef typename base::connections_list connections_list; + using base::m_connected_slots; + + signal8() + { + ; + } + + signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s) + : _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s) + { + ; + } + + template<class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block<mt_policy> lock(this); + _connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn = + new _connection8<desttype, arg1_type, arg2_type, arg3_type, + arg4_type, arg5_type, arg6_type, arg7_type, + arg8_type, mt_policy>(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block<mt_policy> lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // TALK_BASE_SIGSLOT_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socket.h b/Plugins/jingle/libjingle/talk/base/socket.h new file mode 100644 index 0000000..0a30c4b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socket.h @@ -0,0 +1,160 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKET_H__ +#define TALK_BASE_SOCKET_H__ + +#include <errno.h> + +#ifdef POSIX +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#define SOCKET_EACCES EACCES +#endif + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +#include "talk/base/basictypes.h" +#include "talk/base/socketaddress.h" + +// Rather than converting errors into a private namespace, +// Reuse the POSIX socket api errors. Note this depends on +// Win32 compatibility. + +#ifdef WIN32 +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +#undef ENAMETOOLONG // remove errno.h's definition +#define ENAMETOOLONG WSAENAMETOOLONG +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTEMPTY // remove errno.h's definition +#define ENOTEMPTY WSAENOTEMPTY +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE +#undef EACCES +#define SOCKET_EACCES WSAEACCES +#endif // WIN32 + +#ifdef POSIX +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define closesocket(s) close(s) +#endif // POSIX + +namespace talk_base { + +inline bool IsBlockingError(int e) { + return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS); +} + +// General interface for the socket implementations of various networks. The +// methods match those of normal UNIX sockets very closely. +class Socket { +public: + virtual ~Socket() {} + + // Returns the address to which the socket is bound. If the socket is not + // bound, then the any-address is returned. + virtual SocketAddress GetLocalAddress() const = 0; + + // Returns the address to which the socket is connected. If the socket is + // not connected, then the any-address is returned. + virtual SocketAddress GetRemoteAddress() const = 0; + + virtual int Bind(const SocketAddress& addr) = 0; + virtual int Connect(const SocketAddress& addr) = 0; + virtual int Send(const void *pv, size_t cb) = 0; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0; + virtual int Recv(void *pv, size_t cb) = 0; + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) = 0; + virtual int Listen(int backlog) = 0; + virtual Socket *Accept(SocketAddress *paddr) = 0; + virtual int Close() = 0; + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + inline bool IsBlocking() const { return IsBlockingError(GetError()); } + + enum ConnState { + CS_CLOSED, + CS_CONNECTING, + CS_CONNECTED + }; + virtual ConnState GetState() const = 0; + + // Fills in the given uint16 with the current estimate of the MTU along the + // path to the address to which this socket is connected. + virtual int EstimateMTU(uint16* mtu) = 0; + + enum Option { + OPT_DONTFRAGMENT + }; + virtual int SetOption(Option opt, int value) = 0; + +protected: + Socket() {} + +private: + DISALLOW_EVIL_CONSTRUCTORS(Socket); +}; + +} // namespace talk_base + +#endif // TALK_BASE_SOCKET_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketadapters.cc b/Plugins/jingle/libjingle/talk/base/socketadapters.cc new file mode 100644 index 0000000..fcbc20b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketadapters.cc @@ -0,0 +1,677 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <time.h> +#include <errno.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#define _WINSOCKAPI_ +#include <windows.h> +#define SECURITY_WIN32 +#include <security.h> +#endif + +#include "talk/base/basicdefs.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/common.h" +#include "talk/base/httpcommon.h" +#include "talk/base/logging.h" +#include "talk/base/socketadapters.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" + +#ifdef WIN32 +#include "talk/base/sec_buffer.h" +#endif // WIN32 + +namespace talk_base { + +BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size) + : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) { + buffer_ = new char[buffer_size_]; +} + +BufferedReadAdapter::~BufferedReadAdapter() { + delete [] buffer_; +} + +int BufferedReadAdapter::Send(const void *pv, size_t cb) { + if (buffering_) { + // TODO: Spoof error better; Signal Writeable + socket_->SetError(EWOULDBLOCK); + return -1; + } + return AsyncSocketAdapter::Send(pv, cb); +} + +int BufferedReadAdapter::Recv(void *pv, size_t cb) { + if (buffering_) { + socket_->SetError(EWOULDBLOCK); + return -1; + } + + size_t read = 0; + + if (data_len_) { + read = _min(cb, data_len_); + memcpy(pv, buffer_, read); + data_len_ -= read; + if (data_len_ > 0) { + memmove(buffer_, buffer_ + read, data_len_); + } + pv = static_cast<char *>(pv) + read; + cb -= read; + } + + // FIX: If cb == 0, we won't generate another read event + + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res < 0) + return res; + + return res + static_cast<int>(read); +} + +void BufferedReadAdapter::BufferInput(bool on) { + buffering_ = on; +} + +void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) { + ASSERT(socket == socket_); + + if (!buffering_) { + AsyncSocketAdapter::OnReadEvent(socket); + return; + } + + if (data_len_ >= buffer_size_) { + LOG(INFO) << "Input buffer overflow"; + ASSERT(false); + data_len_ = 0; + } + + int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno); + return; + } + + data_len_ += len; + + ProcessInput(buffer_, data_len_); +} + +/////////////////////////////////////////////////////////////////////////////// + +const uint8 SSL_SERVER_HELLO[] = { + 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160, + 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82, + 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91, + 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48, + 50,110,56,77,162,117,87,65,108,52,92,0,4,0 +}; + +const char SSL_CLIENT_HELLO[] = { + -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0, + -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0, + 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22 +}; + +AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) { +} + +int AsyncSSLSocket::Connect(const SocketAddress& addr) { + // Begin buffering before we connect, so that there isn't a race condition between + // potential senders and receiving the OnConnectEvent signal + BufferInput(true); + return BufferedReadAdapter::Connect(addr); +} + +void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) { + ASSERT(socket == socket_); + + // TODO: we could buffer output too... + int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO)); + ASSERT(res == sizeof(SSL_CLIENT_HELLO)); +} + +void AsyncSSLSocket::ProcessInput(char * data, size_t& len) { + if (len < sizeof(SSL_SERVER_HELLO)) + return; + + if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) { + Close(); + SignalCloseEvent(this, 0); // TODO: error code? + return; + } + + len -= sizeof(SSL_SERVER_HELLO); + if (len > 0) { + memmove(data, data + sizeof(SSL_SERVER_HELLO), len); + } + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, + const std::string& user_agent, + const SocketAddress& proxy, + const std::string& username, + const CryptString& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), agent_(user_agent), + user_(username), pass_(password), state_(PS_ERROR), context_(0) { +} + +AsyncHttpsProxySocket::~AsyncHttpsProxySocket() { + delete context_; +} + +int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" + << proxy_.ToString() << ")"; + dest_ = addr; + if (dest_.port() != 80) { + BufferInput(true); + } + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const { + return dest_; +} + +int AsyncHttpsProxySocket::Close() { + headers_.clear(); + state_ = PS_ERROR; + delete context_; + context_ = 0; + return BufferedReadAdapter::Close(); +} + +void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent"; + // TODO: Decide whether tunneling or not should be explicitly set, + // or indicated by destination port (as below) + if (dest_.port() == 80) { + state_ = PS_TUNNEL; + BufferedReadAdapter::OnConnectEvent(socket); + return; + } + SendRequest(); +} + +void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")"; + if ((state_ == PS_WAIT_CLOSE) && (err == 0)) { + state_ = PS_ERROR; + Connect(dest_); + } else { + BufferedReadAdapter::OnCloseEvent(socket, err); + } +} + +void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) { + size_t start = 0; + for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) { + if (state_ == PS_SKIP_BODY) { + size_t consume = _min(len - pos, content_length_); + pos += consume; + start = pos; + content_length_ -= consume; + if (content_length_ == 0) { + EndResponse(); + } + continue; + } + + if (data[pos++] != '\n') + continue; + + size_t len = pos - start - 1; + if ((len > 0) && (data[start + len - 1] == '\r')) + --len; + + data[start + len] = 0; + ProcessLine(data + start, len); + start = pos; + } + + len -= start; + if (len > 0) { + memmove(data, data + start, len); + } + + if (state_ != PS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncHttpsProxySocket::SendRequest() { + std::stringstream ss; + ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n"; + ss << "User-Agent: " << agent_ << "\r\n"; + ss << "Host: " << dest_.IPAsString() << "\r\n"; + ss << "Content-Length: 0\r\n"; + ss << "Proxy-Connection: Keep-Alive\r\n"; + ss << headers_; + ss << "\r\n"; + std::string str = ss.str(); + DirectSend(str.c_str(), str.size()); + state_ = PS_LEADER; + expect_close_ = true; + content_length_ = 0; + headers_.clear(); + + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str; +} + +void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) { + LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data; + + if (len == 0) { + if (state_ == PS_TUNNEL_HEADERS) { + state_ = PS_TUNNEL; + } else if (state_ == PS_ERROR_HEADERS) { + Error(defer_error_); + return; + } else if (state_ == PS_SKIP_HEADERS) { + if (content_length_) { + state_ = PS_SKIP_BODY; + } else { + EndResponse(); + return; + } + } else { + static bool report = false; + if (!unknown_mechanisms_.empty() && !report) { + report = true; + std::string msg( + "Unable to connect to the Google Talk service due to an incompatibility " + "with your proxy.\r\nPlease help us resolve this issue by submitting the " + "following information to us using our technical issue submission form " + "at:\r\n\r\n" + "http://www.google.com/support/talk/bin/request.py\r\n\r\n" + "We apologize for the inconvenience.\r\n\r\n" + "Information to submit to Google: " + ); + //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: "); + msg.append(unknown_mechanisms_); +#ifdef WIN32 + MessageBoxA(0, msg.c_str(), "Oops!", MB_OK); +#endif +#ifdef POSIX + //TODO: Raise a signal or something so the UI can be separated. + LOG(LS_ERROR) << "Oops!\n\n" << msg; +#endif + } + // Unexpected end of headers + Error(0); + return; + } + } else if (state_ == PS_LEADER) { + uint32 code; + if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) { + Error(0); + return; + } + switch (code) { + case 200: + // connection good! + state_ = PS_TUNNEL_HEADERS; + return; +#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407) +#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ +#endif + case 407: // HTTP_STATUS_PROXY_AUTH_REQ + state_ = PS_AUTHENTICATE; + return; + default: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + return; + } + } else if ((state_ == PS_AUTHENTICATE) + && (_strnicmp(data, "Proxy-Authenticate:", 19) == 0)) { + std::string response, auth_method; + switch (HttpAuthenticate(data + 19, len - 19, + proxy_, "CONNECT", "/", + user_, pass_, context_, response, auth_method)) { + case HAR_IGNORE: + LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; + if (!unknown_mechanisms_.empty()) + unknown_mechanisms_.append(", "); + unknown_mechanisms_.append(auth_method); + break; + case HAR_RESPONSE: + headers_ = "Proxy-Authorization: "; + headers_.append(response); + headers_.append("\r\n"); + state_ = PS_SKIP_HEADERS; + unknown_mechanisms_.clear(); + break; + case HAR_CREDENTIALS: + defer_error_ = SOCKET_EACCES; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + case HAR_ERROR: + defer_error_ = 0; + state_ = PS_ERROR_HEADERS; + unknown_mechanisms_.clear(); + break; + } + } else if (_strnicmp(data, "Content-Length:", 15) == 0) { + content_length_ = strtoul(data + 15, 0, 0); + } else if (_strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) { + expect_close_ = false; + /* + } else if (_strnicmp(data, "Connection: close", 17) == 0) { + expect_close_ = true; + */ + } +} + +void AsyncHttpsProxySocket::EndResponse() { + if (!expect_close_) { + SendRequest(); + return; + } + + // No point in waiting for the server to close... let's close now + // TODO: Refactor out PS_WAIT_CLOSE + state_ = PS_WAIT_CLOSE; + BufferedReadAdapter::Close(); + OnCloseEvent(this, 0); +} + +void AsyncHttpsProxySocket::Error(int error) { + BufferInput(false); + Close(); + SetError(error); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const CryptString& password) + : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password), + state_(SS_ERROR) { +} + +int AsyncSocksProxySocket::Connect(const SocketAddress& addr) { + dest_ = addr; + BufferInput(true); + return BufferedReadAdapter::Connect(proxy_); +} + +SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const { + return dest_; +} + +void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) { + SendHello(); +} + +void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) { + ASSERT(state_ < SS_TUNNEL); + + ByteBuffer response(data, len); + + if (state_ == SS_HELLO) { + uint8 ver, method; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(method)) + return; + + if (ver != 5) { + Error(0); + return; + } + + if (method == 0) { + SendConnect(); + } else if (method == 2) { + SendAuth(); + } else { + Error(0); + return; + } + } else if (state_ == SS_AUTH) { + uint8 ver, status; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(status)) + return; + + if ((ver != 1) || (status != 0)) { + Error(SOCKET_EACCES); + return; + } + + SendConnect(); + } else if (state_ == SS_CONNECT) { + uint8 ver, rep, rsv, atyp; + if (!response.ReadUInt8(ver) || + !response.ReadUInt8(rep) || + !response.ReadUInt8(rsv) || + !response.ReadUInt8(atyp)) + return; + + if ((ver != 5) || (rep != 0)) { + Error(0); + return; + } + + uint16 port; + if (atyp == 1) { + uint32 addr; + if (!response.ReadUInt32(addr) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 3) { + uint8 len; + std::string addr; + if (!response.ReadUInt8(len) || + !response.ReadString(addr, len) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port; + } else if (atyp == 4) { + std::string addr; + if (!response.ReadString(addr, 16) || + !response.ReadUInt16(port)) + return; + LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port; + } else { + Error(0); + return; + } + + state_ = SS_TUNNEL; + } + + // Consume parsed data + len = response.Length(); + memcpy(data, response.Data(), len); + + if (state_ != SS_TUNNEL) + return; + + bool remainder = (len > 0); + BufferInput(false); + SignalConnectEvent(this); + + // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble + if (remainder) + SignalReadEvent(this); // TODO: signal this?? +} + +void AsyncSocksProxySocket::SendHello() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + if (user_.empty()) { + request.WriteUInt8(1); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + } else { + request.WriteUInt8(2); // Authentication Mechanisms + request.WriteUInt8(0); // No authentication + request.WriteUInt8(2); // Username/Password + } + DirectSend(request.Data(), request.Length()); + state_ = SS_HELLO; +} + +void AsyncSocksProxySocket::SendAuth() { + ByteBuffer request; + request.WriteUInt8(1); // Negotiation Version + request.WriteUInt8(static_cast<uint8>(user_.size())); + request.WriteString(user_); // Username + request.WriteUInt8(static_cast<uint8>(pass_.GetLength())); + size_t len = pass_.GetLength() + 1; + char * sensitive = new char[len]; + pass_.CopyTo(sensitive, true); + request.WriteString(sensitive); // Password + memset(sensitive, 0, len); + delete [] sensitive; + DirectSend(request.Data(), request.Length()); + state_ = SS_AUTH; +} + +void AsyncSocksProxySocket::SendConnect() { + ByteBuffer request; + request.WriteUInt8(5); // Socks Version + request.WriteUInt8(1); // CONNECT + request.WriteUInt8(0); // Reserved + if (dest_.IsUnresolved()) { + std::string hostname = dest_.IPAsString(); + request.WriteUInt8(3); // DOMAINNAME + request.WriteUInt8(static_cast<uint8>(hostname.size())); + request.WriteString(hostname); // Destination Hostname + } else { + request.WriteUInt8(1); // IPV4 + request.WriteUInt32(dest_.ip()); // Destination IP + } + request.WriteUInt16(dest_.port()); // Destination Port + DirectSend(request.Data(), request.Length()); + state_ = SS_CONNECT; +} + +void AsyncSocksProxySocket::Error(int error) { + state_ = SS_ERROR; + BufferInput(false); + Close(); + SetError(SOCKET_EACCES); + SignalCloseEvent(this, error); +} + +/////////////////////////////////////////////////////////////////////////////// + +LoggingSocketAdapter::LoggingSocketAdapter(AsyncSocket* socket, + LoggingSeverity level, + const char * label, bool hex_mode) +: AsyncSocketAdapter(socket), level_(level), hex_mode_(hex_mode) +{ + label_.append("["); + label_.append(label); + label_.append("]"); +} + +int +LoggingSocketAdapter::Send(const void *pv, size_t cb) { + int res = AsyncSocketAdapter::Send(pv, cb); + if (res > 0) + LogMultiline(level_, label_.c_str(), false, + static_cast<const char *>(pv), res, hex_mode_, &lms_); + return res; +} + +int +LoggingSocketAdapter::SendTo(const void *pv, size_t cb, + const SocketAddress& addr) { + int res = AsyncSocketAdapter::SendTo(pv, cb, addr); + if (res > 0) + LogMultiline(level_, label_.c_str(), false, + static_cast<const char *>(pv), res, hex_mode_, &lms_); + return res; +} + +int +LoggingSocketAdapter::Recv(void *pv, size_t cb) { + int res = AsyncSocketAdapter::Recv(pv, cb); + if (res > 0) + LogMultiline(level_, label_.c_str(), true, + static_cast<const char *>(pv), res, hex_mode_, &lms_); + return res; +} + +int +LoggingSocketAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr); + if (res > 0) + LogMultiline(level_, label_.c_str(), true, + static_cast<const char *>(pv), res, hex_mode_, &lms_); + return res; +} + +void +LoggingSocketAdapter::OnConnectEvent(AsyncSocket * socket) { + LOG_V(level_) << label_ << " Connected"; + AsyncSocketAdapter::OnConnectEvent(socket); +} + +void +LoggingSocketAdapter::OnCloseEvent(AsyncSocket * socket, int err) { + LOG_V(level_) << label_ << " Closed with error: " << err; + AsyncSocketAdapter::OnCloseEvent(socket, err); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/socketadapters.h b/Plugins/jingle/libjingle/talk/base/socketadapters.h new file mode 100644 index 0000000..6cd6a8f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketadapters.h @@ -0,0 +1,170 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETADAPTERS_H__ +#define TALK_BASE_SOCKETADAPTERS_H__ + +#include <map> +#include <string> + +#include "talk/base/asyncsocket.h" +#include "talk/base/cryptstring.h" +#include "talk/base/logging.h" + +namespace talk_base { + +struct HttpAuthContext; + +/////////////////////////////////////////////////////////////////////////////// + +class BufferedReadAdapter : public AsyncSocketAdapter { +public: + BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size); + virtual ~BufferedReadAdapter(); + + virtual int Send(const void *pv, size_t cb); + virtual int Recv(void *pv, size_t cb); + +protected: + int DirectSend(const void *pv, size_t cb) { return AsyncSocketAdapter::Send(pv, cb); } + + void BufferInput(bool on = true); + virtual void ProcessInput(char * data, size_t& len) = 0; + + virtual void OnReadEvent(AsyncSocket * socket); + +private: + char * buffer_; + size_t buffer_size_, data_len_; + bool buffering_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncSSLSocket : public BufferedReadAdapter { +public: + AsyncSSLSocket(AsyncSocket* socket); + + virtual int Connect(const SocketAddress& addr); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void ProcessInput(char * data, size_t& len); +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncHttpsProxySocket : public BufferedReadAdapter { +public: + AsyncHttpsProxySocket(AsyncSocket* socket, const std::string& user_agent, + const SocketAddress& proxy, + const std::string& username, const CryptString& password); + virtual ~AsyncHttpsProxySocket(); + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + virtual int Close(); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void OnCloseEvent(AsyncSocket * socket, int err); + virtual void ProcessInput(char * data, size_t& len); + + void SendRequest(); + void ProcessLine(char * data, size_t len); + void EndResponse(); + void Error(int error); + +private: + SocketAddress proxy_, dest_; + std::string agent_, user_, headers_; + CryptString pass_; + size_t content_length_; + int defer_error_; + bool expect_close_; + enum ProxyState { + PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS, + PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR + } state_; + HttpAuthContext * context_; + std::string unknown_mechanisms_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class AsyncSocksProxySocket : public BufferedReadAdapter { +public: + AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy, + const std::string& username, const CryptString& password); + + virtual int Connect(const SocketAddress& addr); + virtual SocketAddress GetRemoteAddress() const; + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void ProcessInput(char * data, size_t& len); + + void SendHello(); + void SendConnect(); + void SendAuth(); + void Error(int error); + +private: + SocketAddress proxy_, dest_; + std::string user_; + CryptString pass_; + enum SocksState { SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR } state_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class LoggingSocketAdapter : public AsyncSocketAdapter { +public: + LoggingSocketAdapter(AsyncSocket* socket, LoggingSeverity level, + const char * label, bool hex_mode = false); + + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Recv(void *pv, size_t cb); + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr); + +protected: + virtual void OnConnectEvent(AsyncSocket * socket); + virtual void OnCloseEvent(AsyncSocket * socket, int err); + +private: + LoggingSeverity level_; + std::string label_; + bool hex_mode_; + LogMultilineState lms_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETADAPTERS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketaddress.cc b/Plugins/jingle/libjingle/talk/base/socketaddress.cc new file mode 100644 index 0000000..e3a04d1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketaddress.cc @@ -0,0 +1,312 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef POSIX +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#include <cstring> +#include <sstream> + +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/socketaddress.h" + +#ifdef WIN32 +#undef SetPort +int inet_aton(const char * cp, struct in_addr * inp) { + inp->s_addr = inet_addr(cp); + return (inp->s_addr == INADDR_NONE) ? 0 : 1; +} +#endif // WIN32 + +#ifdef _DEBUG +#define DISABLE_DNS 0 +#else // !_DEBUG +#define DISABLE_DNS 0 +#endif // !_DEBUG + +namespace talk_base { + +SocketAddress::SocketAddress() { + Clear(); +} + +SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) { + SetIP(hostname, use_dns); + SetPort(port); +} + +SocketAddress::SocketAddress(uint32 ip, int port) { + SetIP(ip); + SetPort(port); +} + +SocketAddress::SocketAddress(const SocketAddress& addr) { + this->operator=(addr); +} + +void SocketAddress::Clear() { + hostname_.clear(); + ip_ = 0; + port_ = 0; +} + +SocketAddress& SocketAddress::operator=(const SocketAddress& addr) { + hostname_ = addr.hostname_; + ip_ = addr.ip_; + port_ = addr.port_; + return *this; +} + +void SocketAddress::SetIP(uint32 ip) { + hostname_.clear(); + ip_ = ip; +} + +bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) { + hostname_ = hostname; + ip_ = 0; + return Resolve(true, use_dns); +} + +void SocketAddress::SetResolvedIP(uint32 ip) { + ip_ = ip; +} + +void SocketAddress::SetPort(int port) { + ASSERT((0 <= port) && (port < 65536)); + port_ = port; +} + +uint32 SocketAddress::ip() const { + return ip_; +} + +uint16 SocketAddress::port() const { + return port_; +} + +std::string SocketAddress::IPAsString() const { + if (!hostname_.empty()) + return hostname_; + return IPToString(ip_); +} + +std::string SocketAddress::PortAsString() const { + std::ostringstream ost; + ost << port_; + return ost.str(); +} + +std::string SocketAddress::ToString() const { + std::ostringstream ost; + ost << IPAsString(); + ost << ":"; + ost << port(); + return ost.str(); +} + +bool SocketAddress::IsAny() const { + return (ip_ == 0); +} + +bool SocketAddress::IsLocalIP() const { + return (ip_ >> 24) == 127; +} + +bool SocketAddress::IsPrivateIP() const { + return ((ip_ >> 24) == 127) || + ((ip_ >> 24) == 10) || + ((ip_ >> 20) == ((172 << 4) | 1)) || + ((ip_ >> 16) == ((192 << 8) | 168)); +} + +bool SocketAddress::IsUnresolved() const { + return IsAny() && !hostname_.empty(); +} + +bool SocketAddress::Resolve(bool force, bool use_dns) { + if (hostname_.empty()) { + // nothing to resolve + } else if (!force && !IsAny()) { + // already resolved + } else if (uint32 ip = StringToIP(hostname_, use_dns)) { + ip_ = ip; + } else { + return false; + } + return true; +} + +bool SocketAddress::operator ==(const SocketAddress& addr) const { + return EqualIPs(addr) && EqualPorts(addr); +} + +bool SocketAddress::operator <(const SocketAddress& addr) const { + if (ip_ < addr.ip_) + return true; + else if (addr.ip_ < ip_) + return false; + + // We only check hostnames if both IPs are zero. This matches EqualIPs() + if (addr.ip_ == 0) { + if (hostname_ < addr.hostname_) + return true; + else if (addr.hostname_ < hostname_) + return false; + } + + return port_ < addr.port_; +} + +bool SocketAddress::EqualIPs(const SocketAddress& addr) const { + return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_)); +} + +bool SocketAddress::EqualPorts(const SocketAddress& addr) const { + return (port_ == addr.port_); +} + +size_t SocketAddress::Hash() const { + size_t h = 0; + h ^= ip_; + h ^= port_ | (port_ << 16); + return h; +} + +size_t SocketAddress::Size_() const { + return sizeof(ip_) + sizeof(port_); +} + +void SocketAddress::Write_(char* buf, int len) const { + // TODO: Depending on how this is used, we may want/need to write hostname + ASSERT((size_t)len >= Size_()); + reinterpret_cast<uint32*>(buf)[0] = ip_; + buf += sizeof(ip_); + reinterpret_cast<uint16*>(buf)[0] = port_; +} + +void SocketAddress::Read_(const char* buf, int len) { + ASSERT((size_t)len >= Size_()); + ip_ = reinterpret_cast<const uint32*>(buf)[0]; + buf += sizeof(ip_); + port_ = reinterpret_cast<const uint16*>(buf)[0]; +} + +void SocketAddress::ToSockAddr(sockaddr_in* saddr) const { + memset(saddr, 0, sizeof(*saddr)); + saddr->sin_family = AF_INET; + saddr->sin_port = HostToNetwork16(port_); + if (0 == ip_) { + saddr->sin_addr.s_addr = INADDR_ANY; + } else { + saddr->sin_addr.s_addr = HostToNetwork32(ip_); + } +} + +void SocketAddress::FromSockAddr(const sockaddr_in& saddr) { + SetIP(NetworkToHost32(saddr.sin_addr.s_addr)); + SetPort(NetworkToHost16(saddr.sin_port)); +} + +std::string SocketAddress::IPToString(uint32 ip) { + std::ostringstream ost; + ost << ((ip >> 24) & 0xff); + ost << '.'; + ost << ((ip >> 16) & 0xff); + ost << '.'; + ost << ((ip >> 8) & 0xff); + ost << '.'; + ost << ((ip >> 0) & 0xff); + return ost.str(); +} + +uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) { + uint32 ip = 0; + in_addr addr; + if (inet_aton(hostname.c_str(), &addr) != 0) { + ip = NetworkToHost32(addr.s_addr); + } else if (use_dns) { + // Note: this is here so we can spot spurious DNS resolutions for a while + LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ==="; +#if DISABLE_DNS + LOG(WARNING) << "*** DNS DISABLED ***"; +#if WIN32 + WSASetLastError(WSAHOST_NOT_FOUND); +#endif // WIN32 +#endif // DISABLE_DNS + if (hostent * pHost = gethostbyname(hostname.c_str())) { + ip = NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[0])); + } else { +#if WIN32 + LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError(); +#else + LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno); +#endif + } + LOG(INFO) << hostname << " resolved to " << IPToString(ip); + } + return ip; +} + +std::string SocketAddress::GetHostname() { + char hostname[256]; + if (gethostname(hostname, ARRAY_SIZE(hostname)) == 0) + return hostname; + return ""; +} + +bool SocketAddress::GetLocalIPs(std::vector<uint32>& ips) { + ips.clear(); + + const std::string hostname = GetHostname(); + if (hostname.empty()) + return false; + + if (hostent * pHost = gethostbyname(hostname.c_str())) { + for (size_t i=0; pHost->h_addr_list[i]; ++i) { + uint32 ip = + NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[i])); + ips.push_back(ip); + } + return !ips.empty(); + } +#if WIN32 + LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError(); +#else + LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno); +#endif + return false; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/socketaddress.h b/Plugins/jingle/libjingle/talk/base/socketaddress.h new file mode 100644 index 0000000..7fdc22f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketaddress.h @@ -0,0 +1,174 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETADDRESS_H__ +#define TALK_BASE_SOCKETADDRESS_H__ + +#include <string> +#include <vector> +#include "talk/base/basictypes.h" +struct sockaddr_in; + +#undef SetPort + +namespace talk_base { + +// Records an IP address and port, which are 32 and 16 bit integers, +// respectively, both in <b>host byte-order</b>. +class SocketAddress { +public: + // Creates a missing / unknown address. + SocketAddress(); + + // Creates the address with the given host and port. If use_dns is true, + // the hostname will be immediately resolved to an IP (which may block for + // several seconds if DNS is not available). Alternately, set use_dns to + // false, and then call Resolve() to complete resolution later, or use + // SetResolvedIP to set the IP explictly. + SocketAddress(const std::string& hostname, int port = 0, bool use_dns = true); + + // Creates the address with the given IP and port. + SocketAddress(uint32 ip, int port); + + // Creates a copy of the given address. + SocketAddress(const SocketAddress& addr); + + // Resets to missing / unknown address. + void Clear(); + + // Replaces our address with the given one. + SocketAddress& operator =(const SocketAddress& addr); + + // Changes the IP of this address to the given one, and clears the hostname. + void SetIP(uint32 ip); + + // Changes the hostname of this address to the given one. + // Calls Resolve and returns the result. + bool SetIP(const std::string& hostname, bool use_dns = true); + + // Sets the IP address while retaining the hostname. Useful for bypassing + // DNS for a pre-resolved IP. + void SetResolvedIP(uint32 ip); + + // Changes the port of this address to the given one. + void SetPort(int port); + + // Returns the IP address. + uint32 ip() const; + + // Returns the port part of this address. + uint16 port() const; + + // Returns the hostname + const std::string& hostname() const { return hostname_; }; + + // Returns the IP address in dotted form. + std::string IPAsString() const; + + // Returns the port as a string + std::string PortAsString() const; + + // Returns a display version of the IP/port. + std::string ToString() const; + + // Determines whether this represents a missing / any address. + bool IsAny() const; + + // Synomym for missing / any. + bool IsNil() const { return IsAny(); } + + // Determines whether the IP address refers to the local host, i.e. within + // the range 127.0.0.0/8. + bool IsLocalIP() const; + + // Determines whether the IP address is in one of the private ranges: + // 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12. + bool IsPrivateIP() const; + + // Determines whether the hostname has been resolved to an IP + bool IsUnresolved() const; + + // Attempt to resolve a hostname to IP address. + // Returns false if resolution is required but failed. + // 'force' will cause re-resolution of hostname. + // + bool Resolve(bool force = false, bool use_dns = true); + + // Determines whether this address is identical to the given one. + bool operator ==(const SocketAddress& addr) const; + + inline bool operator !=(const SocketAddress& addr) const { + return !this->operator ==(addr); + } + + // Compares based on IP and then port. + bool operator <(const SocketAddress& addr) const; + + // Determines whether this address has the same IP as the one given. + bool EqualIPs(const SocketAddress& addr) const; + + // Deteremines whether this address has the same port as the one given. + bool EqualPorts(const SocketAddress& addr) const; + + // Hashes this address into a small number. + size_t Hash() const; + + // Returns the size of this address when written. + size_t Size_() const; + + // Writes this address into the given buffer. + void Write_(char* buf, int len) const; + + // Reads this address from the given buffer. + void Read_(const char* buf, int len); + + // Convert to and from sockaddr_in + void ToSockAddr(sockaddr_in* saddr) const; + void FromSockAddr(const sockaddr_in& saddr); + + // Converts the IP address given in compact form into dotted form. + static std::string IPToString(uint32 ip); + + // Converts the IP address given in dotted form into compact form. + // Without 'use_dns', only dotted names (A.B.C.D) are resolved. + static uint32 StringToIP(const std::string& str, bool use_dns = true); + + // Get local machine's hostname + static std::string GetHostname(); + + // Get a list of the local machine's ip addresses + static bool GetLocalIPs(std::vector<uint32>& ips); + +private: + std::string hostname_; + uint32 ip_; + uint16 port_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETADDRESS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc b/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc new file mode 100644 index 0000000..7f190a9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketaddresspair.cc @@ -0,0 +1,58 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/socketaddresspair.h" + +namespace talk_base { + +SocketAddressPair::SocketAddressPair( + const SocketAddress& src, const SocketAddress& dest) + : src_(src), dest_(dest) { +} + + +bool SocketAddressPair::operator ==(const SocketAddressPair& p) const { + return (src_ == p.src_) && (dest_ == p.dest_); +} + +bool SocketAddressPair::operator <(const SocketAddressPair& p) const { + if (src_ < p.src_) + return true; + if (p.src_ < src_) + return false; + if (dest_ < p.dest_) + return true; + if (p.dest_ < dest_) + return false; + return false; +} + +size_t SocketAddressPair::Hash() const { + return src_.Hash() ^ dest_.Hash(); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/socketaddresspair.h b/Plugins/jingle/libjingle/talk/base/socketaddresspair.h new file mode 100644 index 0000000..10f5d30 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketaddresspair.h @@ -0,0 +1,58 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETADDRESSPAIR_H__ +#define TALK_BASE_SOCKETADDRESSPAIR_H__ + +#include "talk/base/socketaddress.h" + +namespace talk_base { + +// Records a pair (source,destination) of socket addresses. The two addresses +// identify a connection between two machines. (For UDP, this "connection" is +// not maintained explicitly in a socket.) +class SocketAddressPair { +public: + SocketAddressPair() {} + SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest); + + const SocketAddress& source() const { return src_; } + const SocketAddress& destination() const { return dest_; } + + bool operator ==(const SocketAddressPair& r) const; + bool operator <(const SocketAddressPair& r) const; + + size_t Hash() const; + +private: + SocketAddress src_; + SocketAddress dest_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETADDRESSPAIR_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketfactory.h b/Plugins/jingle/libjingle/talk/base/socketfactory.h new file mode 100644 index 0000000..2a4aee2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketfactory.h @@ -0,0 +1,51 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETFACTORY_H__ +#define TALK_BASE_SOCKETFACTORY_H__ + +#include "talk/base/socket.h" +#include "talk/base/asyncsocket.h" + +namespace talk_base { + +class SocketFactory { +public: + virtual ~SocketFactory() {} + + // Returns a new socket for blocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + virtual Socket* CreateSocket(int type) = 0; + + // Returns a new socket for nonblocking communication. The type can be + // SOCK_DGRAM and SOCK_STREAM. + virtual AsyncSocket* CreateAsyncSocket(int type) = 0; +}; + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETFACTORY_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketpool.cc b/Plugins/jingle/libjingle/talk/base/socketpool.cc new file mode 100644 index 0000000..614ce89 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketpool.cc @@ -0,0 +1,263 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <errno.h> + +#include "talk/base/asyncsocket.h" +#include "talk/base/logging.h" +#include "talk/base/socketfactory.h" +#include "talk/base/socketpool.h" +#include "talk/base/socketstream.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// StreamCache - Caches a set of open streams, defers creation to a separate +// StreamPool. +/////////////////////////////////////////////////////////////////////////////// + +StreamCache::StreamCache(StreamPool* pool) : pool_(pool) { +} + +StreamCache::~StreamCache() { + for (ConnectedList::iterator it = active_.begin(); it != active_.end(); + ++it) { + delete it->second; + } + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + delete it->second; + } +} + +StreamInterface* StreamCache::RequestConnectedStream( + const SocketAddress& remote, int* err) { + LOG_F(LS_VERBOSE) << "(" << remote.ToString() << ")"; + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + if (remote == it->first) { + it->second->SignalEvent.disconnect(this); + // Move from cached_ to active_ + active_.push_front(*it); + cached_.erase(it); + if (err) + *err = 0; + LOG_F(LS_VERBOSE) << "Providing cached stream"; + return active_.front().second; + } + } + if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) { + // We track active streams so that we can remember their address + active_.push_front(ConnectedStream(remote, stream)); + LOG_F(LS_VERBOSE) << "Providing new stream"; + return active_.front().second; + } + return NULL; +} + +void StreamCache::ReturnConnectedStream(StreamInterface* stream) { + for (ConnectedList::iterator it = active_.begin(); it != active_.end(); + ++it) { + if (stream == it->second) { + LOG_F(LS_VERBOSE) << "(" << it->first.ToString() << ")"; + if (stream->GetState() == SS_CLOSED) { + // Return closed streams + LOG_F(LS_VERBOSE) << "Returning closed stream"; + pool_->ReturnConnectedStream(it->second); + } else { + // Monitor open streams + stream->SignalEvent.connect(this, &StreamCache::OnStreamEvent); + LOG_F(LS_VERBOSE) << "Caching stream"; + cached_.push_front(*it); + } + active_.erase(it); + return; + } + } + ASSERT(false); +} + +void StreamCache::OnStreamEvent(StreamInterface* stream, int events, int err) { + if ((events & SE_CLOSE) == 0) { + LOG_F(LS_WARNING) << "(" << events << ", " << err + << ") received non-close event"; + return; + } + for (ConnectedList::iterator it = cached_.begin(); it != cached_.end(); + ++it) { + if (stream == it->second) { + LOG_F(LS_VERBOSE) << "(" << it->first.ToString() << ")"; + // We don't cache closed streams, so return it. + it->second->SignalEvent.disconnect(this); + LOG_F(LS_VERBOSE) << "Returning closed stream"; + pool_->ReturnConnectedStream(it->second); + cached_.erase(it); + return; + } + } + ASSERT(false); +} + +////////////////////////////////////////////////////////////////////// +// NewSocketPool +////////////////////////////////////////////////////////////////////// + +NewSocketPool::NewSocketPool(SocketFactory* factory) : factory_(factory) { +} + +NewSocketPool::~NewSocketPool() { + for (size_t i = 0; i < used_.size(); ++i) { + delete used_[i]; + } +} + +StreamInterface* +NewSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) { + AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM); + if (!socket) { + ASSERT(false); + if (err) + *err = -1; + return NULL; + } + if ((socket->Connect(remote) != 0) && !socket->IsBlocking()) { + if (err) + *err = socket->GetError(); + delete socket; + return NULL; + } + if (err) + *err = 0; + return new SocketStream(socket); +} + +void +NewSocketPool::ReturnConnectedStream(StreamInterface* stream) { + used_.push_back(stream); +} + +////////////////////////////////////////////////////////////////////// +// ReuseSocketPool +////////////////////////////////////////////////////////////////////// + +ReuseSocketPool::ReuseSocketPool(SocketFactory* factory, AsyncSocket* socket) + : factory_(factory), stream_(NULL) { + stream_ = socket ? new SocketStream(socket) : NULL; +} + +ReuseSocketPool::~ReuseSocketPool() { + delete stream_; +} + +void +ReuseSocketPool::setSocket(AsyncSocket* socket) { + ASSERT(false); // TODO: need ref-counting to make this work + delete stream_; + stream_ = socket ? new SocketStream(socket) : NULL; +} + +StreamInterface* +ReuseSocketPool::RequestConnectedStream(const SocketAddress& remote, int* err) { + if (!stream_) { + LOG(LS_INFO) << "ReuseSocketPool - Creating new socket"; + AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM); + if (!socket) { + ASSERT(false); + if (err) + *err = -1; + return NULL; + } + stream_ = new SocketStream(socket); + } + if ((stream_->GetState() == SS_OPEN) && + (stream_->GetSocket()->GetRemoteAddress() == remote)) { + LOG(LS_INFO) << "ReuseSocketPool - Reusing connection to: " + << remote.ToString(); + } else { + stream_->Close(); + if ((stream_->GetSocket()->Connect(remote) != 0) + && !stream_->GetSocket()->IsBlocking()) { + if (err) + *err = stream_->GetSocket()->GetError(); + return NULL; + } else { + LOG(LS_INFO) << "ReuseSocketPool - Opening connection to: " + << remote.ToString(); + } + } + if (err) + *err = 0; + return stream_; +} + +void +ReuseSocketPool::ReturnConnectedStream(StreamInterface* stream) { + // Note: this might not be true with the advent of setSocket + ASSERT(stream == stream_); +} + +/////////////////////////////////////////////////////////////////////////////// +// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached +// LoggingAdapters. +/////////////////////////////////////////////////////////////////////////////// + +LoggingPoolAdapter::LoggingPoolAdapter( + StreamPool* pool, LoggingSeverity level, const std::string& label, + bool binary_mode) + : pool_(pool), level_(level), label_(label), binary_mode_(binary_mode) { +} + +LoggingPoolAdapter::~LoggingPoolAdapter() { + for (StreamList::iterator it = recycle_bin_.begin(); + it != recycle_bin_.end(); ++it) { + delete *it; + } +} + +StreamInterface* LoggingPoolAdapter::RequestConnectedStream( + const SocketAddress& remote, int* err) { + if (StreamInterface* stream = pool_->RequestConnectedStream(remote, err)) { + if (recycle_bin_.empty()) { + return new LoggingAdapter(stream, level_, label_, binary_mode_); + } + LoggingAdapter* logging = recycle_bin_.front(); + recycle_bin_.pop_front(); + logging->Attach(stream); + return logging; + } + return NULL; +} + +void LoggingPoolAdapter::ReturnConnectedStream(StreamInterface* stream) { + LoggingAdapter* logging = static_cast<LoggingAdapter*>(stream); + pool_->ReturnConnectedStream(logging->Detach()); + recycle_bin_.push_back(logging); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/socketpool.h b/Plugins/jingle/libjingle/talk/base/socketpool.h new file mode 100644 index 0000000..edeebaa --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketpool.h @@ -0,0 +1,161 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETPOOL_H__ +#define TALK_BASE_SOCKETPOOL_H__ + +#include <deque> +#include <vector> +#include "talk/base/logging.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +class AsyncSocket; +class LoggingAdapter; +class SocketAddress; +class SocketFactory; +class SocketStream; +class StreamInterface; + +////////////////////////////////////////////////////////////////////// +// StreamPool +////////////////////////////////////////////////////////////////////// + +class StreamPool { +public: + virtual ~StreamPool() { } + + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err) = 0; + virtual void ReturnConnectedStream(StreamInterface* stream) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamCache - Caches a set of open streams, defers creation/destruction to +// the supplied StreamPool. +/////////////////////////////////////////////////////////////////////////////// + +class StreamCache : public StreamPool, public sigslot::has_slots<> { +public: + StreamCache(StreamPool* pool); + virtual ~StreamCache(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + typedef std::pair<SocketAddress, StreamInterface*> ConnectedStream; + typedef std::list<ConnectedStream> ConnectedList; + + void OnStreamEvent(StreamInterface* stream, int events, int err); + + // We delegate stream creation and deletion to this pool. + StreamPool* pool_; + // Streams that are in use (returned from RequestConnectedStream). + ConnectedList active_; + // Streams which were returned to us, but are still open. + ConnectedList cached_; +}; + +////////////////////////////////////////////////////////////////////// +// NewSocketPool // +/////////////////// +// Creates a new PTcpSocket every time +////////////////////////////////////////////////////////////////////// + +class NewSocketPool : public StreamPool { +public: + NewSocketPool(SocketFactory* factory); + virtual ~NewSocketPool(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + SocketFactory* factory_; + std::vector<StreamInterface*> used_; +}; + +////////////////////////////////////////////////////////////////////// +// ReuseSocketPool // +///////////////////// +// Pass a PTcpSocket chain to the constructor, and if the connection +// is still open, it will be reused. +////////////////////////////////////////////////////////////////////// + +class ReuseSocketPool : public StreamPool { +public: + ReuseSocketPool(SocketFactory* factory, AsyncSocket* socket = 0); + virtual ~ReuseSocketPool(); + + void setSocket(AsyncSocket* socket); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + SocketFactory* factory_; + SocketStream* stream_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// LoggingPoolAdapter - Adapts a StreamPool to supply streams with attached +// LoggingAdapters. +/////////////////////////////////////////////////////////////////////////////// + +class LoggingPoolAdapter : public StreamPool { +public: + LoggingPoolAdapter(StreamPool* pool, LoggingSeverity level, + const std::string& label, bool binary_mode); + virtual ~LoggingPoolAdapter(); + + // StreamPool Interface + virtual StreamInterface* RequestConnectedStream(const SocketAddress& remote, + int* err); + virtual void ReturnConnectedStream(StreamInterface* stream); + +private: + StreamPool* pool_; + LoggingSeverity level_; + std::string label_; + bool binary_mode_; + typedef std::deque<LoggingAdapter*> StreamList; + StreamList recycle_bin_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETPOOL_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketserver.h b/Plugins/jingle/libjingle/talk/base/socketserver.h new file mode 100644 index 0000000..e06dd6b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketserver.h @@ -0,0 +1,57 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKETSERVER_H__ +#define TALK_BASE_SOCKETSERVER_H__ + +#include "talk/base/socketfactory.h" + +namespace talk_base { + +const int kForever = -1; + +// Provides the ability to wait for activity on a set of sockets. The Thread +// class provides a nice wrapper on a socket server. +// +// The server is also a socket factory. The sockets it creates will be +// notified of asynchronous I/O from this server's Wait method. +class SocketServer : public SocketFactory { +public: + + // Sleeps until: + // 1) cms milliseconds have elapsed (unless cms == kForever) + // 2) WakeUp() is called + // While sleeping, I/O is performed if process_io is true. + virtual bool Wait(int cms, bool process_io) = 0; + + // Causes the current wait (if one is in progress) to wake up. + virtual void WakeUp() = 0; +}; + +} // namespace talk_base + +#endif // TALK_BASE_SOCKETSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/socketstream.h b/Plugins/jingle/libjingle/talk/base/socketstream.h new file mode 100644 index 0000000..4d56b75 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/socketstream.h @@ -0,0 +1,153 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SOCKET_STREAM_H__ +#define TALK_BASE_SOCKET_STREAM_H__ + +#include "talk/base/asyncsocket.h" +#include "talk/base/common.h" +#include "talk/base/stream.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +class SocketStream : public StreamInterface, public sigslot::has_slots<> { + public: + SocketStream(AsyncSocket* socket) : socket_(NULL) { + Attach(socket); + } + virtual ~SocketStream() { delete socket_; } + + void Attach(AsyncSocket* socket) { + if (socket_) + delete socket_; + socket_ = socket; + if (socket_) { + socket_->SignalConnectEvent.connect(this, &SocketStream::OnConnectEvent); + socket_->SignalReadEvent.connect(this, &SocketStream::OnReadEvent); + socket_->SignalWriteEvent.connect(this, &SocketStream::OnWriteEvent); + socket_->SignalCloseEvent.connect(this, &SocketStream::OnCloseEvent); + } + } + + AsyncSocket* Detach() { + AsyncSocket* socket = socket_; + if (socket_) { + socket_->SignalConnectEvent.disconnect(this); + socket_->SignalReadEvent.disconnect(this); + socket_->SignalWriteEvent.disconnect(this); + socket_->SignalCloseEvent.disconnect(this); + socket_ = NULL; + } + return socket; + } + + AsyncSocket* GetSocket() { return socket_; } + + virtual StreamState GetState() const { + ASSERT(socket_ != NULL); + switch (socket_->GetState()) { + case Socket::CS_CONNECTED: + return SS_OPEN; + case Socket::CS_CONNECTING: + return SS_OPENING; + case Socket::CS_CLOSED: + default: + return SS_CLOSED; + } + } + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + ASSERT(socket_ != NULL); + int result = socket_->Recv(buffer, buffer_len); + if (result < 0) { + if (socket_->IsBlocking()) + return SR_BLOCK; + if (error) + *error = socket_->GetError(); + return SR_ERROR; + } + if ((result > 0) || (buffer_len == 0)) { + if (read) + *read = result; + return SR_SUCCESS; + } + return SR_EOS; + } + + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + ASSERT(socket_ != NULL); + int result = socket_->Send(data, data_len); + if (result < 0) { + if (socket_->IsBlocking()) + return SR_BLOCK; + if (error) + *error = socket_->GetError(); + return SR_ERROR; + } + if (written) + *written = result; + return SR_SUCCESS; + } + + virtual void Close() { ASSERT(socket_ != NULL); socket_->Close(); } + + virtual bool GetSize(size_t* size) const { return false; } + virtual bool ReserveSize(size_t size) { return true; } + virtual bool Rewind() { return false; } + + private: + void OnConnectEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_OPEN | SE_READ | SE_WRITE, 0); + } + void OnReadEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_READ, 0); + } + void OnWriteEvent(AsyncSocket* socket) { + ASSERT(socket == socket_); + SignalEvent(this, SE_WRITE, 0); + } + void OnCloseEvent(AsyncSocket* socket, int err) { + ASSERT(socket == socket_); + SignalEvent(this, SE_CLOSE, err); + } + + AsyncSocket* socket_; + + DISALLOW_EVIL_CONSTRUCTORS(SocketStream); +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_SOCKET_STREAM_H__ diff --git a/Plugins/jingle/libjingle/talk/base/ssladapter.cc b/Plugins/jingle/libjingle/talk/base/ssladapter.cc new file mode 100644 index 0000000..68fe038 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/ssladapter.cc @@ -0,0 +1,191 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/ssladapter.h" + +#if !defined(SSL_USE_SCHANNEL) && !defined(SSL_USE_OPENSSL) +#ifdef WIN32 +#define SSL_USE_SCHANNEL 1 +#else // !WIN32 +#define SSL_USE_OPENSSL 1 +#endif // !WIN32 +#endif + +#if SSL_USE_SCHANNEL +#include "schanneladapter.h" +namespace talk_base { + typedef SChannelAdapter DefaultSSLAdapter; +} +#endif // SSL_USE_SCHANNEL + +#if SSL_USE_OPENSSL +#include <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> +#include "openssladapter.h" +namespace talk_base { + typedef OpenSSLAdapter DefaultSSLAdapter; +} +#if defined(WIN32) + #define MUTEX_TYPE HANDLE + #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL) + #define MUTEX_CLEANUP(x) CloseHandle(x) + #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) + #define MUTEX_UNLOCK(x) ReleaseMutex(x) + #define THREAD_ID GetCurrentThreadId() +#elif defined(_POSIX_THREADS) + // _POSIX_THREADS is normally defined in unistd.h if pthreads are available + // on your platform. + #define MUTEX_TYPE pthread_mutex_t + #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) + #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) + #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) + #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) + #define THREAD_ID pthread_self() +#else + #error You must define mutex operations appropriate for your platform! +#endif + +struct CRYPTO_dynlock_value { + MUTEX_TYPE mutex; +}; + +#endif // SSL_USE_OPENSSL + +/////////////////////////////////////////////////////////////////////////////// + +namespace talk_base { + +SSLAdapter* +SSLAdapter::Create(AsyncSocket* socket) { + return new DefaultSSLAdapter(socket); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if SSL_USE_OPENSSL + + + +// This array will store all of the mutexes available to OpenSSL. +static MUTEX_TYPE* mutex_buf = NULL; + +static void locking_function(int mode, int n, const char * file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(mutex_buf[n]); + } else { + MUTEX_UNLOCK(mutex_buf[n]); + } +} + +static pthread_t id_function() { + return THREAD_ID; +} + +static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) { + CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value; + if (!value) + return NULL; + MUTEX_SETUP(value->mutex); + return value; +} + +static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l, + const char* file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(l->mutex); + } else { + MUTEX_UNLOCK(l->mutex); + } +} + +static void dyn_destroy_function(CRYPTO_dynlock_value* l, + const char* file, int line) { + MUTEX_CLEANUP(l->mutex); + delete l; +} + +bool InitializeSSL() { + if (!InitializeSSLThread() || !SSL_library_init()) + return false; + SSL_load_error_strings(); + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + RAND_poll(); + return true; +} + +bool InitializeSSLThread() { + mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; + if (!mutex_buf) + return false; + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_SETUP(mutex_buf[i]); + + // we need to cast our id_function to return an unsigned long -- pthread_t is a pointer + CRYPTO_set_id_callback((unsigned long (*)())id_function); + CRYPTO_set_locking_callback(locking_function); + CRYPTO_set_dynlock_create_callback(dyn_create_function); + CRYPTO_set_dynlock_lock_callback(dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); + return true; +} + +bool CleanupSSL() { + if (!mutex_buf) + return false; + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_CLEANUP(mutex_buf[i]); + delete [] mutex_buf; + mutex_buf = NULL; + return true; +} + +#else // !SSL_USE_OPENSSL + +bool InitializeSSL() { + return true; +} + +bool InitializeSSLThread() { + return true; +} + +bool CleanupSSL() { + return true; +} + +#endif // !SSL_USE_OPENSSL + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/ssladapter.h b/Plugins/jingle/libjingle/talk/base/ssladapter.h new file mode 100644 index 0000000..0f0155c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/ssladapter.h @@ -0,0 +1,74 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_SSLADAPTER_H__ +#define TALK_BASE_SSLADAPTER_H__ + +#include "talk/base/asyncsocket.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +class SSLAdapter : public AsyncSocketAdapter { +public: + SSLAdapter(AsyncSocket* socket) + : AsyncSocketAdapter(socket), ignore_bad_cert_(false) { } + + bool ignore_bad_cert() const { return ignore_bad_cert_; } + void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; } + + // StartSSL returns 0 if successful. + // If StartSSL is called while the socket is closed or connecting, the SSL + // negotiation will begin as soon as the socket connects. + virtual int StartSSL(const char* hostname, bool restartable) = 0; + + // Create the default SSL adapter for this platform + static SSLAdapter* Create(AsyncSocket* socket); + +private: + // If true, the server certificate need not match the configured hostname. + bool ignore_bad_cert_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Call this on the main thread, before using SSL. +// Call CleanupSSLThread when finished with SSL. +bool InitializeSSL(); + +// Call to initialize additional threads. +bool InitializeSSLThread(); + +// Call to cleanup additional threads, and also the main thread. +bool CleanupSSL(); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_SSLADAPTER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/stl_decl.h b/Plugins/jingle/libjingle/talk/base/stl_decl.h new file mode 100644 index 0000000..193e7af --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stl_decl.h @@ -0,0 +1,84 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_STL_DECL_H__ +#define TALK_BASE_STL_DECL_H__ + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0 +#pragma warning(disable:4786) +#endif + +#include <sys/types.h> + +namespace std { + template <class Key> struct hash; + template <class Key> struct equal_to; + template <class Key> struct less; + template <class T> class allocator; + template <class Key, class Val, + class Compare, + class Alloc> class map; + template <class T, class Alloc> class vector; + template <class T, class Alloc> class list; + template <class T, class Alloc> class slist; + template <class T, class Sequence> class stack; + template <class T, class Sequence> class queue; + template <class T, class Sequence, class Compare> class priority_queue; + template <class T1, class T2> struct pair; + template <class Key, class Compare, class Alloc> class set; +} + +///////////////////////////////////////////////////////////////////////////// +// Workaround declaration problem with defaults +///////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0 + +#define STD_MAP(T1, T2) \ + std::map<T1 , T2, std::less<T1>, std::allocator<T2> > + +#define STD_VECTOR(T1) \ + std::vector<T1, std::allocator<T1> > + +#define STD_SET(T1) \ + std::set<T1, std::less<T1>, std::allocator<T1> > + +#else + +#define STD_MAP(T1, T2) \ + std::map<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2 > > > + +#define STD_VECTOR(T1) \ + std::vector<T1, std::allocator<T1> > + +#define STD_SET(T1) \ + std::set<T1, std::less<T1>, std::allocator<T1> > + +#endif + + +#endif // TALK_BASE_STL_DECL_H__ diff --git a/Plugins/jingle/libjingle/talk/base/stream.cc b/Plugins/jingle/libjingle/talk/base/stream.cc new file mode 100644 index 0000000..50d49a6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stream.cc @@ -0,0 +1,664 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <string> +#include "talk/base/basictypes.h" +#include "talk/base/common.h" +#include "talk/base/stream.h" +#include "talk/base/stringencode.h" + +#ifdef WIN32 +#include "talk/base/win32.h" +#define fileno _fileno +#endif + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +StreamResult StreamInterface::WriteAll(const void* data, size_t data_len, + size_t* written, int* error) { + StreamResult result = SR_SUCCESS; + size_t total_written = 0, current_written; + while (total_written < data_len) { + result = Write(static_cast<const char*>(data) + total_written, + data_len - total_written, ¤t_written, error); + if (result != SR_SUCCESS) + break; + total_written += current_written; + } + if (written) + *written = total_written; + return result; +} + +StreamResult StreamInterface::ReadAll(void* buffer, size_t buffer_len, + size_t* read, int* error) { + StreamResult result = SR_SUCCESS; + size_t total_read = 0, current_read; + while (total_read < buffer_len) { + result = Read(static_cast<char*>(buffer) + total_read, + buffer_len - total_read, ¤t_read, error); + if (result != SR_SUCCESS) + break; + total_read += current_read; + } + if (read) + *read = total_read; + return result; +} + +StreamResult StreamInterface::ReadLine(std::string* line) { + StreamResult result = SR_SUCCESS; + while (true) { + char ch; + result = Read(&ch, sizeof(ch), NULL, NULL); + if (result != SR_SUCCESS) { + break; + } + if (ch == '\n') { + break; + } + line->push_back(ch); + } + if (!line->empty()) { // give back the line we've collected so far with + result = SR_SUCCESS; // a success code. Otherwise return the last code + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamTap +/////////////////////////////////////////////////////////////////////////////// + +StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap) +: StreamAdapterInterface(stream), tap_(NULL), tap_result_(SR_SUCCESS), + tap_error_(0) +{ + AttachTap(tap); +} + +void StreamTap::AttachTap(StreamInterface* tap) { + tap_.reset(tap); +} + +StreamInterface* StreamTap::DetachTap() { + return tap_.release(); +} + +StreamResult StreamTap::GetTapResult(int* error) { + if (error) { + *error = tap_error_; + } + return tap_result_; +} + +StreamResult StreamTap::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t backup_read; + if (!read) { + read = &backup_read; + } + StreamResult res = StreamAdapterInterface::Read(buffer, buffer_len, + read, error); + if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) { + tap_result_ = tap_->WriteAll(buffer, *read, NULL, &tap_error_); + } + return res; +} + +StreamResult StreamTap::Write(const void* data, size_t data_len, + size_t* written, int* error) { + size_t backup_written; + if (!written) { + written = &backup_written; + } + StreamResult res = StreamAdapterInterface::Write(data, data_len, + written, error); + if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) { + tap_result_ = tap_->WriteAll(data, *written, NULL, &tap_error_); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +// NullStream +/////////////////////////////////////////////////////////////////////////////// + +NullStream::NullStream() { +} + +NullStream::~NullStream() { +} + +StreamState NullStream::GetState() const { + return SS_OPEN; +} + +StreamResult NullStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (error) *error = -1; + return SR_ERROR; +} + + +StreamResult NullStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (written) *written = data_len; + return SR_SUCCESS; +} + +void NullStream::Close() { +} + +bool NullStream::GetSize(size_t* size) const { + if (size) + *size = 0; + return true; +} + +bool NullStream::ReserveSize(size_t size) { + return true; +} + +bool NullStream::Rewind() { + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// FileStream +/////////////////////////////////////////////////////////////////////////////// + +FileStream::FileStream() : file_(NULL) { +} + +FileStream::~FileStream() { + FileStream::Close(); +} + +bool FileStream::Open(const std::string& filename, const char* mode) { + Close(); +#ifdef WIN32 + int filenamelen = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), + filename.length() + 1, NULL, 0); + int modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); + wchar_t *wfilename = new wchar_t[filenamelen+4]; // 4 for "\\?\" + wchar_t *wfilename_dest = wfilename; + wchar_t *wmode = new wchar_t[modelen]; + + if (!filename.empty() && (filename[0] != '\\')) { + wcscpy(wfilename, L"\\\\?\\"); + wfilename_dest = wfilename + 4; + } + + if ((MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.length() + 1, + wfilename_dest, filenamelen) > 0) && + (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen) > 0)) { + file_ = _wfopen(wfilename, wmode); + } else { + file_ = NULL; + } + + delete[] wfilename; + delete[] wmode; +#else + file_ = fopen(filename.c_str(), mode); +#endif + return (file_ != NULL); +} + +bool FileStream::OpenShare(const std::string& filename, const char* mode, + int shflag) { + Close(); +#ifdef WIN32 + int filenamelen = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), + filename.length() + 1, NULL, 0); + int modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); + wchar_t *wfilename = new wchar_t[filenamelen+4]; // 4 for "\\?\" + wchar_t *wfilename_dest = wfilename; + wchar_t *wmode = new wchar_t[modelen]; + + if (!filename.empty() && (filename[0] != '\\')) { + wcscpy(wfilename, L"\\\\?\\"); + wfilename_dest = wfilename + 4; + } + + if ((MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.length() + 1, + wfilename_dest, filenamelen) > 0) && + (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen) > 0)) { + file_ = _wfsopen(wfilename, wmode, shflag); + } else { + file_ = NULL; + } + + delete[] wfilename; + delete[] wmode; +#else + return Open(filename, mode); +#endif + return (file_ != NULL); +} + +bool FileStream::DisableBuffering() { + if (!file_) + return false; + return (setvbuf(file_, NULL, _IONBF, 0) == 0); +} + +StreamState FileStream::GetState() const { + return (file_ == NULL) ? SS_CLOSED : SS_OPEN; +} + +StreamResult FileStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (!file_) + return SR_EOS; + size_t result = fread(buffer, 1, buffer_len, file_); + if ((result == 0) && (buffer_len > 0)) { + if (feof(file_)) + return SR_EOS; + if (error) + *error = errno; + return SR_ERROR; + } + if (read) + *read = result; + return SR_SUCCESS; +} + +StreamResult FileStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (!file_) + return SR_EOS; + size_t result = fwrite(data, 1, data_len, file_); + if ((result == 0) && (data_len > 0)) { + if (error) + *error = errno; + return SR_ERROR; + } + if (written) + *written = result; + return SR_SUCCESS; +} + +void FileStream::Close() { + if (file_) { + fclose(file_); + file_ = NULL; + } +} + +bool FileStream::SetPosition(size_t position) { + if (!file_) + return false; + return (fseek(file_, position, SEEK_SET) == 0); +} + +bool FileStream::GetPosition(size_t * position) const { + ASSERT(position != NULL); + if (!file_ || !position) + return false; + long result = ftell(file_); + if (result < 0) + return false; + *position = result; + return true; +} + +bool FileStream::GetSize(size_t * size) const { + ASSERT(size != NULL); + if (!file_ || !size) + return false; + struct stat file_stats; + if (fstat(fileno(file_), &file_stats) != 0) + return false; + *size = file_stats.st_size; + return true; +} + +bool FileStream::ReserveSize(size_t size) { + // TODO: extend the file to the proper length + return true; +} + +bool FileStream::GetSize(const std::string& filename, size_t* size) { + struct stat file_stats; + if (stat(filename.c_str(), &file_stats) != 0) + return false; + *size = file_stats.st_size; + return true; +} + +int FileStream::Flush() { + if (file_) { + return fflush (file_); + } + // try to flush empty file? + ASSERT(false); + return 0; +} +/////////////////////////////////////////////////////////////////////////////// + + +MemoryStream::MemoryStream() + : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) { +} + +MemoryStream::MemoryStream(const char* data) + : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) { + SetContents(data, strlen(data)); +} + +MemoryStream::MemoryStream(const char* data, size_t length) + : allocated_length_(0), buffer_(NULL), data_length_(0), seek_position_(0) { + SetContents(data, length); +} + +MemoryStream::~MemoryStream() { + delete [] buffer_; +} + +void MemoryStream::SetContents(const char* data, size_t length) { + delete [] buffer_; + data_length_ = allocated_length_ = length; + buffer_ = new char[allocated_length_]; + memcpy(buffer_, data, data_length_); +} + +StreamState MemoryStream::GetState() const { + return SS_OPEN; +} + +StreamResult MemoryStream::Read(void *buffer, size_t bytes, + size_t *bytes_read, int *error) { + if (seek_position_ >= data_length_) { + // At end of stream + if (error) { + *error = EOF; + } + return SR_EOS; + } + + size_t remaining_length = data_length_ - seek_position_; + if (bytes > remaining_length) { + // Read partial buffer + bytes = remaining_length; + } + memcpy(buffer, &buffer_[seek_position_], bytes); + seek_position_ += bytes; + if (bytes_read) { + *bytes_read = bytes; + } + return SR_SUCCESS; +} + +StreamResult MemoryStream::Write(const void *buffer, + size_t bytes, size_t *bytes_written, int *error) { + StreamResult sr = SR_SUCCESS; + int error_value = 0; + size_t bytes_written_value = 0; + + size_t new_position = seek_position_ + bytes; + if (new_position > allocated_length_) { + // Increase buffer size to the larger of: + // a) new position rounded up to next 256 bytes + // b) double the previous length + size_t new_allocated_length = _max((new_position | 0xFF) + 1, + allocated_length_ * 2); + if (char* new_buffer = new char[new_allocated_length]) { + memcpy(new_buffer, buffer_, data_length_); + delete [] buffer_; + buffer_ = new_buffer; + allocated_length_ = new_allocated_length; + } else { + error_value = ENOMEM; + sr = SR_ERROR; + } + } + + if (sr == SR_SUCCESS) { + bytes_written_value = bytes; + memcpy(&buffer_[seek_position_], buffer, bytes); + seek_position_ = new_position; + if (data_length_ < seek_position_) { + data_length_ = seek_position_; + } + } + + if (bytes_written) { + *bytes_written = bytes_written_value; + } + if (error) { + *error = error_value; + } + + return sr; +} + +void MemoryStream::Close() { + // nothing to do +} + +bool MemoryStream::SetPosition(size_t position) { + if (position <= data_length_) { + seek_position_ = position; + return true; + } + return false; +} + +bool MemoryStream::GetPosition(size_t *position) const { + if (!position) { + return false; + } + *position = seek_position_; + return true; +} + +bool MemoryStream::GetSize(size_t *size) const { + if (!size) { + return false; + } + *size = data_length_; + return true; +} + +bool MemoryStream::ReserveSize(size_t size) { + if (allocated_length_ >= size) + return true; + + if (char* new_buffer = new char[size]) { + memcpy(new_buffer, buffer_, data_length_); + delete [] buffer_; + buffer_ = new_buffer; + allocated_length_ = size; + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +StreamResult Flow(StreamInterface* source, + char* buffer, size_t buffer_len, + StreamInterface* sink) { + ASSERT(buffer_len > 0); + + StreamResult result; + size_t count, read_pos, write_pos; + + bool end_of_stream = false; + do { + // Read until buffer is full, end of stream, or error + read_pos = 0; + do { + result = source->Read(buffer + read_pos, buffer_len - read_pos, + &count, NULL); + if (result == SR_EOS) { + end_of_stream = true; + } else if (result != SR_SUCCESS) { + return result; + } else { + read_pos += count; + } + } while (!end_of_stream && (read_pos < buffer_len)); + + // Write until buffer is empty, or error (including end of stream) + write_pos = 0; + do { + result = sink->Write(buffer + write_pos, read_pos - write_pos, + &count, NULL); + if (result != SR_SUCCESS) + return result; + + write_pos += count; + } while (write_pos < read_pos); + } while (!end_of_stream); + + return SR_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// + +LoggingAdapter::LoggingAdapter(StreamInterface* stream, LoggingSeverity level, + const std::string& label, bool hex_mode) +: StreamAdapterInterface(stream), level_(level), hex_mode_(hex_mode) +{ + label_.append("["); + label_.append(label); + label_.append("]"); +} + +StreamResult LoggingAdapter::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t local_read; if (!read) read = &local_read; + StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, read, error); + if (result == SR_SUCCESS) { + LogMultiline(level_, label_.c_str(), true, + static_cast<const char *>(buffer), *read, hex_mode_, &lms_); + } + return result; +} + +StreamResult LoggingAdapter::Write(const void* data, size_t data_len, + size_t* written, int* error) { + size_t local_written; if (!written) written = &local_written; + StreamResult result = StreamAdapterInterface::Write(data, data_len, written, error); + if (result == SR_SUCCESS) { + LogMultiline(level_, label_.c_str(), false, + static_cast<const char *>(data), *written, hex_mode_, &lms_); + } + return result; +} + +void LoggingAdapter::Close() { + LOG_V(level_) << label_ << " Closed locally"; + StreamAdapterInterface::Close(); +} + +void LoggingAdapter::OnEvent(StreamInterface* stream, int events, int err) { + if (events & SE_OPEN) { + LOG_V(level_) << label_ << " Open"; + } else if (events & SE_CLOSE) { + LOG_V(level_) << label_ << " Closed with error: " << err; + } + StreamAdapterInterface::OnEvent(stream, events, err); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream - Reads/Writes to an external std::string +/////////////////////////////////////////////////////////////////////////////// + +StringStream::StringStream(std::string& str) +: str_(str), read_pos_(0), read_only_(false) +{ +} + +StringStream::StringStream(const std::string& str) +: str_(const_cast<std::string&>(str)), read_pos_(0), read_only_(true) +{ +} + +StreamState StringStream::GetState() const { + return SS_OPEN; +} + +StreamResult StringStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t available = talk_base::_min(buffer_len, str_.size() - read_pos_); + if (!available) + return SR_EOS; + memcpy(buffer, str_.data() + read_pos_, available); + read_pos_ += available; + if (read) + *read = available; + return SR_SUCCESS; +} + +StreamResult StringStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (read_only_) { + if (error) { + *error = -1; + } + return SR_ERROR; + } + str_.append(static_cast<const char*>(data), + static_cast<const char*>(data) + data_len); + if (written) + *written = data_len; + return SR_SUCCESS; +} + +void StringStream::Close() { +} + +bool StringStream::GetSize(size_t* size) const { + ASSERT(size != NULL); + *size = str_.size(); + return true; +} + +bool StringStream::ReserveSize(size_t size) { + if (read_only_) + return false; + str_.reserve(size); + return true; +} + +bool StringStream::Rewind() { + read_pos_ = 0; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/stream.h b/Plugins/jingle/libjingle/talk/base/stream.h new file mode 100644 index 0000000..cb91bb7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stream.h @@ -0,0 +1,396 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_STREAM_H__ +#define TALK_BASE_STREAM_H__ + +#include "talk/base/basictypes.h" +#include "talk/base/logging.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// StreamInterface is a generic asynchronous stream interface, supporting read, +// write, and close operations, and asynchronous signalling of state changes. +// The interface is designed with file, memory, and socket implementations in +// mind. +/////////////////////////////////////////////////////////////////////////////// + +// The following enumerations are declared outside of the StreamInterface +// class for brevity in use. + +// The SS_OPENING state indicates that the stream will signal open or closed +// in the future. +enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN }; + +// Stream read/write methods return this value to indicate various success +// and failure conditions described below. +enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS }; + +// StreamEvents are used to asynchronously signal state transitionss. The flags +// may be combined. +// SE_OPEN: The stream has transitioned to the SS_OPEN state +// SE_CLOSE: The stream has transitioned to the SS_CLOSED state +// SE_READ: Data is available, so Read is likely to not return SR_BLOCK +// SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK +enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 }; + +class StreamInterface { + public: + virtual ~StreamInterface() { } + + virtual StreamState GetState() const = 0; + + // Read attempts to fill buffer of size buffer_len. Write attempts to send + // data_len bytes stored in data. The variables read and write are set only + // on SR_SUCCESS (see below). Likewise, error is only set on SR_ERROR. + // Read and Write return a value indicating: + // SR_ERROR: an error occurred, which is returned in a non-null error + // argument. Interpretation of the error requires knowledge of the + // stream's concrete type, which limits its usefulness. + // SR_SUCCESS: some number of bytes were successfully written, which is + // returned in a non-null read/write argument. + // SR_BLOCK: the stream is in non-blocking mode, and the operation would + // block, or the stream is in SS_OPENING state. + // SR_EOS: the end-of-stream has been reached, or the stream is in the + // SS_CLOSED state. + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) = 0; + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) = 0; + + // Attempt to transition to the SS_CLOSED state. SE_CLOSE will not be + // signalled as a result of this call. + virtual void Close() = 0; + + // Return the number of bytes that will be returned by Read, if known. + virtual bool GetSize(size_t* size) const = 0; + + // Communicates the amount of data which will be written to the stream. The + // stream may choose to preallocate memory to accomodate this data. The + // stream may return false to indicate that there is not enough room (ie, + // Write will return SR_EOS/SR_ERROR at some point). Note that calling this + // function should not affect the existing state of data in the stream. + virtual bool ReserveSize(size_t size) = 0; + + // Returns true if stream could be repositioned to the beginning. + virtual bool Rewind() = 0; + + // WriteAll is a helper function which repeatedly calls Write until all the + // data is written, or something other than SR_SUCCESS is returned. Note that + // unlike Write, the argument 'written' is always set, and may be non-zero + // on results other than SR_SUCCESS. The remaining arguments have the + // same semantics as Write. + StreamResult WriteAll(const void* data, size_t data_len, + size_t* written, int* error); + + // Similar to ReadAll. Calls Read until buffer_len bytes have been read, or + // until a non-SR_SUCCESS result is returned. 'read' is always set. + StreamResult ReadAll(void* buffer, size_t buffer_len, + size_t* read, int* error); + + // ReadLine is a helper function which repeatedly calls Read until it hits + // the end-of-line character, or something other than SR_SUCCESS. + // TODO: this is too inefficient to keep here. Break this out into a buffered + // readline object or adapter + StreamResult ReadLine(std::string *line); + + // Streams may signal one or more StreamEvents to indicate state changes. + // The first argument identifies the stream on which the state change occured. + // The second argument is a bit-wise combination of StreamEvents. + // If SE_CLOSE is signalled, then the third argument is the associated error + // code. Otherwise, the value is undefined. + // Note: Not all streams will support asynchronous event signalling. However, + // SS_OPENING and SR_BLOCK returned from stream member functions imply that + // certain events will be raised in the future. + sigslot::signal3<StreamInterface*, int, int> SignalEvent; + + protected: + StreamInterface() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StreamInterface); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamAdapterInterface is a convenient base-class for adapting a stream. +// By default, all operations are pass-through. Override the methods that you +// require adaptation. Note that the adapter will delete the adapted stream. +/////////////////////////////////////////////////////////////////////////////// + +class StreamAdapterInterface : public StreamInterface, + public sigslot::has_slots<> { + public: + explicit StreamAdapterInterface(StreamInterface* stream) { + Attach(stream); + } + + virtual StreamState GetState() const { + return stream_->GetState(); + } + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + return stream_->Read(buffer, buffer_len, read, error); + } + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error) { + return stream_->Write(data, data_len, written, error); + } + virtual void Close() { + stream_->Close(); + } + virtual bool GetSize(size_t* size) const { + return stream_->GetSize(size); + } + virtual bool ReserveSize(size_t size) { + return stream_->ReserveSize(size); + } + virtual bool Rewind() { + return stream_->Rewind(); + } + + void Attach(StreamInterface* stream) { + if (NULL != stream_.get()) + stream_->SignalEvent.disconnect(this); + stream_.reset(stream); + if (NULL != stream_.get()) + stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent); + } + StreamInterface* Detach() { + if (NULL == stream_.get()) + return NULL; + stream_->SignalEvent.disconnect(this); + return stream_.release(); + } + + protected: + // Note that the adapter presents itself as the origin of the stream events, + // since users of the adapter may not recognize the adapted object. + virtual void OnEvent(StreamInterface* stream, int events, int err) { + SignalEvent(this, events, err); + } + + private: + scoped_ptr<StreamInterface> stream_; + DISALLOW_EVIL_CONSTRUCTORS(StreamAdapterInterface); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamTap is a non-modifying, pass-through adapter, which copies all data +// in either direction to the tap. Note that errors or blocking on writing to +// the tap will prevent further tap writes from occurring. +/////////////////////////////////////////////////////////////////////////////// + +class StreamTap : public StreamAdapterInterface { + public: + explicit StreamTap(StreamInterface* stream, StreamInterface* tap); + + void AttachTap(StreamInterface* tap); + StreamInterface* DetachTap(); + StreamResult GetTapResult(int* error); + + // StreamAdapterInterface Interface + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + private: + scoped_ptr<StreamInterface> tap_; + StreamResult tap_result_; + int tap_error_; + DISALLOW_EVIL_CONSTRUCTORS(StreamTap); +}; + +/////////////////////////////////////////////////////////////////////////////// +// NullStream gives errors on read, and silently discards all written data. +/////////////////////////////////////////////////////////////////////////////// + +class NullStream : public StreamInterface { + public: + NullStream(); + virtual ~NullStream(); + + // StreamInterface Interface + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool GetSize(size_t* size) const; + virtual bool ReserveSize(size_t size); + virtual bool Rewind(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// FileStream is a simple implementation of a StreamInterface, which does not +// support asynchronous notification. +/////////////////////////////////////////////////////////////////////////////// + +class FileStream : public StreamInterface { + public: + FileStream(); + virtual ~FileStream(); + + // The semantics of filename and mode are the same as stdio's fopen + virtual bool Open(const std::string& filename, const char* mode); + virtual bool OpenShare(const std::string& filename, const char* mode, + int shflag); + + // By default, reads and writes are buffered for efficiency. Disabling + // buffering causes writes to block until the bytes on disk are updated. + virtual bool DisableBuffering(); + + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool GetSize(size_t* size) const; + virtual bool ReserveSize(size_t size); + virtual bool Rewind() { return SetPosition(0); } + + bool SetPosition(size_t position); + bool GetPosition(size_t* position) const; + int Flush(); + static bool GetSize(const std::string& filename, size_t* size); + + private: + FILE* file_; + DISALLOW_EVIL_CONSTRUCTORS(FileStream); +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryStream is a simple implementation of a StreamInterface over in-memory +// data. It does not support asynchronous notification. +/////////////////////////////////////////////////////////////////////////////// + +class MemoryStream : public StreamInterface { + public: + MemoryStream(); + // Pre-populate stream with the provided data. + MemoryStream(const char* data); + MemoryStream(const char* data, size_t length); + virtual ~MemoryStream(); + + virtual StreamState GetState() const; + virtual StreamResult Read(void *buffer, size_t bytes, size_t *bytes_read, int *error); + virtual StreamResult Write(const void *buffer, size_t bytes, size_t *bytes_written, int *error); + virtual void Close(); + virtual bool GetSize(size_t* size) const; + virtual bool ReserveSize(size_t size); + virtual bool Rewind() { return SetPosition(0); } + + char* GetBuffer() { return buffer_; } + const char* GetBuffer() const { return buffer_; } + bool SetPosition(size_t position); + bool GetPosition(size_t* position) const; + + private: + void SetContents(const char* data, size_t length); + + size_t allocated_length_; + char* buffer_; + size_t data_length_; + size_t seek_position_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(MemoryStream); +}; + +/////////////////////////////////////////////////////////////////////////////// + +class LoggingAdapter : public StreamAdapterInterface { +public: + LoggingAdapter(StreamInterface* stream, LoggingSeverity level, + const std::string& label, bool hex_mode = false); + + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + + protected: + virtual void OnEvent(StreamInterface* stream, int events, int err); + + private: + LoggingSeverity level_; + std::string label_; + bool hex_mode_; + LogMultilineState lms_; + + DISALLOW_EVIL_CONSTRUCTORS(LoggingAdapter); +}; + +/////////////////////////////////////////////////////////////////////////////// +// StringStream - Reads/Writes to an external std::string +/////////////////////////////////////////////////////////////////////////////// + +class StringStream : public StreamInterface { +public: + StringStream(std::string& str); + StringStream(const std::string& str); + + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + virtual bool GetSize(size_t* size) const; + virtual bool ReserveSize(size_t size); + virtual bool Rewind(); + +private: + std::string& str_; + size_t read_pos_; + bool read_only_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Flow attempts to move bytes from source to sink via buffer of size +// buffer_len. The function returns SR_SUCCESS when source reaches +// end-of-stream (returns SR_EOS), and all the data has been written successful +// to sink. Alternately, if source returns SR_BLOCK or SR_ERROR, or if sink +// returns SR_BLOCK, SR_ERROR, or SR_EOS, then the function immediately returns +// with the unexpected StreamResult value. + +StreamResult Flow(StreamInterface* source, + char* buffer, size_t buffer_len, + StreamInterface* sink); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_STREAM_H__ diff --git a/Plugins/jingle/libjingle/talk/base/streamutils.cc b/Plugins/jingle/libjingle/talk/base/streamutils.cc new file mode 100644 index 0000000..52e6da7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/streamutils.cc @@ -0,0 +1,194 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + + +#include "talk/base/common.h" +#include "talk/base/streamutils.h" + +/////////////////////////////////////////////////////////////////////////////// +// TODO: Extend so that one side can close, and other side can send +// buffered data. + +StreamRelay::StreamRelay(talk_base::StreamInterface* s1, + talk_base::StreamInterface* s2, + size_t buffer_size) : buffer_size_(buffer_size) { + dir_[0].stream = s1; + dir_[1].stream = s2; + + ASSERT(s1->GetState() != talk_base::SS_CLOSED); + ASSERT(s2->GetState() != talk_base::SS_CLOSED); + + for (size_t i=0; i<2; ++i) { + dir_[i].stream->SignalEvent.connect(this, &StreamRelay::OnEvent); + dir_[i].buffer = new char[buffer_size_]; + dir_[i].data_len = 0; + } +} + +StreamRelay::~StreamRelay() { + for (size_t i=0; i<2; ++i) { + delete dir_[i].stream; + delete [] dir_[i].buffer; + } +} + +void +StreamRelay::Circulate() { + int error = 0; + if (!Flow(0, &error) || !Flow(1, &error)) { + Close(); + SignalClosed(this, error); + } +} + +void +StreamRelay::Close() { + for (size_t i=0; i<2; ++i) { + dir_[i].stream->SignalEvent.disconnect(this); + dir_[i].stream->Close(); + } +} + +bool +StreamRelay::Flow(int read_index, int* error) { + Direction& reader = dir_[read_index]; + Direction& writer = dir_[Complement(read_index)]; + + bool progress; + do { + progress = false; + + while (reader.stream->GetState() == talk_base::SS_OPEN) { + size_t available = buffer_size_ - reader.data_len; + if (available == 0) + break; + + *error = 0; + size_t read = 0; + talk_base::StreamResult result + = reader.stream->Read(reader.buffer + reader.data_len, available, + &read, error); + if ((result == talk_base::SR_BLOCK) || (result == talk_base::SR_EOS)) + break; + + if (result == talk_base::SR_ERROR) + return false; + + progress = true; + ASSERT((read > 0) && (read <= available)); + reader.data_len += read; + } + + size_t total_written = 0; + while (writer.stream->GetState() == talk_base::SS_OPEN) { + size_t available = reader.data_len - total_written; + if (available == 0) + break; + + *error = 0; + size_t written = 0; + talk_base::StreamResult result + = writer.stream->Write(reader.buffer + total_written, + available, &written, error); + if ((result == talk_base::SR_BLOCK) || (result == talk_base::SR_EOS)) + break; + + if (result == talk_base::SR_ERROR) + return false; + + progress = true; + ASSERT((written > 0) && (written <= available)); + total_written += written; + } + + reader.data_len -= total_written; + if (reader.data_len > 0) { + memmove(reader.buffer, reader.buffer + total_written, reader.data_len); + } + } while (progress); + + return true; +} + +void StreamRelay::OnEvent(talk_base::StreamInterface* stream, int events, + int error) { + int index = Index(stream); + + // Note: In the following cases, we are treating the open event as both + // readable and writeable, for robustness. It won't hurt if we are wrong. + + if ((events & talk_base::SE_OPEN | talk_base::SE_READ) + && !Flow(index, &error)) { + events = talk_base::SE_CLOSE; + } + + if ((events & talk_base::SE_OPEN | talk_base::SE_WRITE) + && !Flow(Complement(index), &error)) { + events = talk_base::SE_CLOSE; + } + + if (events & talk_base::SE_CLOSE) { + Close(); + SignalClosed(this, error); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// StreamCounter - counts the number of bytes which are transferred in either +// direction. +/////////////////////////////////////////////////////////////////////////////// + +StreamCounter::StreamCounter(talk_base::StreamInterface* stream) + : StreamAdapterInterface(stream), count_(0) { +} + +talk_base::StreamResult StreamCounter::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + size_t tmp; + if (!read) + read = &tmp; + talk_base::StreamResult result + = StreamAdapterInterface::Read(buffer, buffer_len, + read, error); + if (result == talk_base::SR_SUCCESS) + count_ += *read; + SignalUpdateByteCount(count_); + return result; +} + +talk_base::StreamResult StreamCounter::Write( + const void* data, size_t data_len, size_t* written, int* error) { + size_t tmp; + if (!written) + written = &tmp; + talk_base::StreamResult result + = StreamAdapterInterface::Write(data, data_len, written, error); + if (result == talk_base::SR_SUCCESS) + count_ += *written; + SignalUpdateByteCount(count_); + return result; +} diff --git a/Plugins/jingle/libjingle/talk/base/streamutils.h b/Plugins/jingle/libjingle/talk/base/streamutils.h new file mode 100644 index 0000000..7bb07a3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/streamutils.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_APP_STREAMUTILS_H__ +#define TALK_APP_STREAMUTILS_H__ + +#include "talk/base/sigslot.h" +#include "talk/base/stream.h" + +/////////////////////////////////////////////////////////////////////////////// +// StreamRelay - acts as an intermediary between two asynchronous streams, +// reading from one stream and writing to the other, using a pre-specified +// amount of buffering in both directions. +/////////////////////////////////////////////////////////////////////////////// + +class StreamRelay : public sigslot::has_slots<> { +public: + StreamRelay(talk_base::StreamInterface* s1, + talk_base::StreamInterface* s2, size_t buffer_size); + virtual ~StreamRelay(); + + void Circulate(); // Simulate events to get things flowing + void Close(); + + sigslot::signal2<StreamRelay*, int> SignalClosed; + +private: + inline int Index(talk_base::StreamInterface* s) const + { return (s == dir_[1].stream); } + inline int Complement(int index) const { return (1-index); } + + bool Flow(int read_index, int* error); + void OnEvent(talk_base::StreamInterface* stream, int events, int error); + + struct Direction { + talk_base::StreamInterface* stream; + char* buffer; + size_t data_len; + }; + Direction dir_[2]; + size_t buffer_size_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamCounter - counts the number of bytes which are transferred in either +// direction. +/////////////////////////////////////////////////////////////////////////////// + +class StreamCounter : public talk_base::StreamAdapterInterface { + public: + explicit StreamCounter(talk_base::StreamInterface* stream); + + inline void ResetByteCount() { count_ = 0; } + inline size_t GetByteCount() const { return count_; } + + sigslot::signal1<size_t> SignalUpdateByteCount; + + // StreamAdapterInterface + virtual talk_base::StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual talk_base::StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + private: + size_t count_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#endif // TALK_APP_STREAMUTILS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/stringdigest.cc b/Plugins/jingle/libjingle/talk/base/stringdigest.cc new file mode 100644 index 0000000..1f98124 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringdigest.cc @@ -0,0 +1,49 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + + +#include "talk/base/md5.h" +#include "talk/base/stringdigest.h" +#include "talk/base/stringencode.h" + +namespace talk_base { + +std::string MD5(const std::string& data) { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(data.data())), static_cast<unsigned int>(data.size())); + unsigned char digest[16]; + MD5Final(digest, &ctx); + std::string hex_digest; + for (int i=0; i<16; ++i) { + hex_digest += hex_encode(digest[i] >> 4); + hex_digest += hex_encode(digest[i] & 0xf); + } + return hex_digest; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/stringdigest.h b/Plugins/jingle/libjingle/talk/base/stringdigest.h new file mode 100644 index 0000000..d75d845 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringdigest.h @@ -0,0 +1,47 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + + +#ifndef TALK_BASE_STRINGDIGEST_H__ +#define TALK_BASE_STRINGDIGEST_H__ + +#include <string> + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// Message Digest Utilities +////////////////////////////////////////////////////////////////////// + +// Compute the MD5 message digest of data, and return it in +std::string MD5(const std::string& data); + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_STRINGDIGEST_H__ diff --git a/Plugins/jingle/libjingle/talk/base/stringencode.cc b/Plugins/jingle/libjingle/talk/base/stringencode.cc new file mode 100644 index 0000000..ed6edfc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringencode.cc @@ -0,0 +1,579 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef WIN32 +#include <malloc.h> +#endif // WIN32 +#ifdef POSIX +#include <alloca.h> +#define _alloca alloca +#endif // POSIX + +#include "talk/base/basictypes.h" +#include "talk/base/common.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" + +namespace talk_base { + +///////////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +///////////////////////////////////////////////////////////////////////////// + +static const char HEX[] = "0123456789abcdef"; + +char hex_encode(unsigned char val) { + ASSERT(val < 16); + return (val < 16) ? HEX[val] : '!'; +} + +unsigned char hex_decode(char ch) { + char lower = tolower(ch); + ASSERT(((ch >= '0') && (ch <= '9')) || ((lower >= 'a') && (lower <= 'z'))); + return (ch <= '9') ? (ch - '0') : ((lower - 'a') + 10); +} + +size_t escape(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) || ::strchr(illegal, ch)) { + if (bufpos + 2 >= buflen) + break; + buffer[bufpos++] = escape; + } + buffer[bufpos++] = ch; + } + + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t unescape(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) && (srcpos < srclen)) { + ch = source[srcpos++]; + } + buffer[bufpos++] = ch; + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t encode(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch != escape) && !::strchr(illegal, ch)) { + buffer[bufpos++] = ch; + } else if (bufpos + 3 >= buflen) { + break; + } else { + buffer[bufpos+0] = escape; + buffer[bufpos+1] = hex_encode((static_cast<unsigned char>(ch) >> 4) & 0xF); + buffer[bufpos+2] = hex_encode((static_cast<unsigned char>(ch) ) & 0xF); + bufpos += 3; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t decode(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape) { + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + char ch = source[srcpos++]; + if ((ch == escape) && (srcpos + 1 < srclen)) { + buffer[bufpos++] = (hex_decode(source[srcpos]) << 4) + | hex_decode(source[srcpos+1]); + srcpos += 2; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +const char* unsafe_filename_characters() { + // It might be better to have a single specification which is the union of + // all operating systems, unless one system is overly restrictive. +#ifdef WIN32 + return "\\/:*?\"<>|"; +#else // !WIN32 + // TODO +#endif // !WIN23 +} + +const unsigned char URL_UNSAFE = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127 +const unsigned char XML_UNSAFE = 0x2; // "&'<> +const unsigned char HTML_UNSAFE = 0x2; // "&'<> + +// ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ? +//@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ +//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ + +const unsigned char ASCII_CLASS[128] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1, +}; + +size_t url_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + if (NULL == buffer) + return srclen * 3 + 1; + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) { + if (bufpos + 3 >= buflen) { + break; + } + buffer[bufpos+0] = '%'; + buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos+2] = hex_encode((ch ) & 0xF); + bufpos += 3; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t url_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + if (NULL == buffer) + return srclen + 1; + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if (ch == '+') { + buffer[bufpos++] = ' '; + } else if ((ch == '%') && (srcpos + 1 < srclen)) { + buffer[bufpos++] = (hex_decode(source[srcpos]) << 4) + | hex_decode(source[srcpos+1]); + srcpos += 2; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) { + const unsigned char* s = reinterpret_cast<const unsigned char*>(source); + if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx + *value = s[0]; + return 1; + } + if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx + return 0; + } + // Accumulate the trailer byte values in value16, and combine it with the + // relevant bits from s[0], once we've determined the sequence length. + unsigned long value16 = (s[1] & 0x3F); + if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx + *value = ((s[0] & 0x1F) << 6) | value16; + return 2; + } + if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx + return 0; + } + value16 = (value16 << 6) | (s[2] & 0x3F); + if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx + *value = ((s[0] & 0x0F) << 12) | value16; + return 3; + } + if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx + return 0; + } + value16 = (value16 << 6) | (s[3] & 0x3F); + if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx + *value = ((s[0] & 0x07) << 18) | value16; + return 4; + } + return 0; +} + +size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) { + if ((value <= 0x7F) && (buflen >= 1)) { + buffer[0] = static_cast<unsigned char>(value); + return 1; + } + if ((value <= 0x7FF) && (buflen >= 2)) { + buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6); + buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F); + return 2; + } + if ((value <= 0xFFFF) && (buflen >= 3)) { + buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12); + buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F); + buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F); + return 3; + } + if ((value <= 0x1FFFFF) && (buflen >= 4)) { + buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18); + buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F); + buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F); + buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F); + return 4; + } + return 0; +} + +size_t html_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos]; + if (ch < 128) { + srcpos += 1; + if (ASCII_CLASS[ch] & HTML_UNSAFE) { + const char * escseq = 0; + size_t esclen = 0; + switch (ch) { + case '<': escseq = "<"; esclen = 4; break; + case '>': escseq = ">"; esclen = 4; break; + case '\'': escseq = "'"; esclen = 5; break; + case '\"': escseq = """; esclen = 6; break; + case '&': escseq = "&"; esclen = 5; break; + default: ASSERT(false); + } + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } else { + buffer[bufpos++] = ch; + } + } else { + // Largest value is 0x1FFFFF => � (10 characters) + char escseq[11]; + unsigned long val; + if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) { + srcpos += vallen; + } else { + // Not a valid utf8 sequence, just use the raw character. + val = static_cast<unsigned char>(source[srcpos++]); + } + size_t esclen = sprintfn(escseq, ARRAY_SIZE(escseq), "&#%lu;", val); + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t html_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + ASSERT(NULL != buffer); // TODO: estimate output size + return xml_decode(buffer, buflen, source, srclen); +} + +size_t xml_encode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) { + const char * escseq = 0; + size_t esclen = 0; + switch (ch) { + case '<': escseq = "<"; esclen = 4; break; + case '>': escseq = ">"; esclen = 4; break; + case '\'': escseq = "'"; esclen = 6; break; + case '\"': escseq = """; esclen = 6; break; + case '&': escseq = "&"; esclen = 5; break; + default: ASSERT(false); + } + if (bufpos + esclen >= buflen) { + break; + } + memcpy(buffer + bufpos, escseq, esclen); + bufpos += esclen; + } else { + buffer[bufpos++] = ch; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t xml_decode(char * buffer, size_t buflen, + const char * source, size_t srclen) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + size_t srcpos = 0, bufpos = 0; + while ((srcpos < srclen) && (bufpos + 1 < buflen)) { + unsigned char ch = source[srcpos++]; + if (ch != '&') { + buffer[bufpos++] = ch; + } else if ((srcpos + 2 < srclen) + && (memcmp(source + srcpos, "lt;", 3) == 0)) { + buffer[bufpos++] = '<'; + srcpos += 3; + } else if ((srcpos + 2 < srclen) + && (memcmp(source + srcpos, "gt;", 3) == 0)) { + buffer[bufpos++] = '>'; + srcpos += 3; + } else if ((srcpos + 4 < srclen) + && (memcmp(source + srcpos, "apos;", 5) == 0)) { + buffer[bufpos++] = '\''; + srcpos += 5; + } else if ((srcpos + 4 < srclen) + && (memcmp(source + srcpos, "quot;", 5) == 0)) { + buffer[bufpos++] = '\"'; + srcpos += 5; + } else if ((srcpos + 3 < srclen) + && (memcmp(source + srcpos, "amp;", 4) == 0)) { + buffer[bufpos++] = '&'; + srcpos += 4; + } else if ((srcpos < srclen) && (source[srcpos] == '#')) { + int int_base = 10; + if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) { + int_base = 16; + srcpos += 1; + } + char * ptr; + // TODO: Fix hack (ptr may go past end of data) + unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base); + if ((static_cast<size_t>(ptr - source) < srclen) && (*ptr == ';')) { + srcpos = ptr - source + 1; + } else { + // Not a valid escape sequence. + break; + } + if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) { + bufpos += esclen; + } else { + // Not enough room to encode the character, or illegal character + break; + } + } else { + // Unrecognized escape sequence. + break; + } + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t hex_encode(char * buffer, size_t buflen, + const char * csource, size_t srclen) { + ASSERT(NULL != buffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + const unsigned char * bsource = + reinterpret_cast<const unsigned char *>(csource); + + size_t srcpos = 0, bufpos = 0; + srclen = _min(srclen, (buflen - 1) / 2); + while (srcpos < srclen) { + unsigned char ch = bsource[srcpos++]; + buffer[bufpos ] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos+1] = hex_encode((ch ) & 0xF); + bufpos += 2; + } + buffer[bufpos] = '\0'; + return bufpos; +} + +size_t hex_decode(char * cbuffer, size_t buflen, + const char * source, size_t srclen) { + ASSERT(NULL != cbuffer); // TODO: estimate output size + if (buflen <= 0) + return 0; + + unsigned char * bbuffer = reinterpret_cast<unsigned char *>(cbuffer); + + size_t srcpos = 0, bufpos = 0; + while ((srcpos + 1 < srclen) && (bufpos + 1 < buflen)) { + unsigned char v1 = (hex_decode(source[srcpos]) << 4); + unsigned char v2 = hex_decode(source[srcpos+1]); + bbuffer[bufpos++] = v1 | v2; + srcpos += 2; + } + bbuffer[bufpos] = '\0'; + return bufpos; +} + +void transform(std::string& value, size_t maxlen, const std::string& source, + Transform t) { + char * buffer = static_cast<char *>(_alloca(maxlen + 1)); + value.assign(buffer, t(buffer, maxlen + 1, source.data(), source.length())); +} + +std::string s_transform(const std::string& source, Transform t) { + // Ask transformation function to approximate the destination size (returns upper bound) + size_t maxlen = t(NULL, 0, source.data(), source.length()); + char * buffer = static_cast<char *>(_alloca(maxlen)); + size_t len = t(buffer, maxlen, source.data(), source.length()); + std::string result(buffer, len); + return result; +} + +char make_char_safe_for_filename(char c) { + if (c < 32) + return '_'; + + switch (c) { + case '<': + case '>': + case ':': + case '"': + case '/': + case '\\': + case '|': + case '*': + case '?': + return '_'; + + default: + return c; + } +} + +/* +void sprintf(std::string& value, size_t maxlen, const char * format, ...) { + char * buffer = static_cast<char *>(alloca(maxlen + 1)); + va_list args; + va_start(args, format); + value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args)); + va_end(args); +} +*/ + +///////////////////////////////////////////////////////////////////////////// +// Unit Tests +///////////////////////////////////////////////////////////////////////////// + +#ifdef _DEBUG + +static int utf8_unittest() { + const struct Utf8Test { + const char* encoded; + size_t encsize, enclen; + unsigned long decoded; + } kTests[] = { + { "a ", 5, 1, 'a' }, + { "\x7F ", 5, 1, 0x7F }, + { "\xC2\x80 ", 5, 2, 0x80 }, + { "\xDF\xBF ", 5, 2, 0x7FF }, + { "\xE0\xA0\x80 ", 5, 3, 0x800 }, + { "\xEF\xBF\xBF ", 5, 3, 0xFFFF }, + { "\xF0\x90\x80\x80 ", 5, 4, 0x10000 }, + { "\xF0\x90\x80\x80 ", 3, 0, 0x10000 }, + { "\xF0\xF0\x80\x80 ", 5, 0, 0 }, + { "\xF0\x90\x80 ", 5, 0, 0 }, + { "\x90\x80\x80 ", 5, 0, 0 }, + { NULL, 0, 0 }, + }; + for (size_t i=0; kTests[i].encoded; ++i) { + unsigned long val = 0; + ASSERT(kTests[i].enclen == utf8_decode(kTests[i].encoded, + kTests[i].encsize, + &val)); + unsigned long result = (kTests[i].enclen == 0) ? 0 : kTests[i].decoded; + ASSERT(val == result); + + if (kTests[i].decoded == 0) { + // Not an interesting encoding test case + continue; + } + + char buffer[5]; + memset(buffer, 0x01, ARRAY_SIZE(buffer)); + ASSERT(kTests[i].enclen == utf8_encode(buffer, + kTests[i].encsize, + kTests[i].decoded)); + ASSERT(memcmp(buffer, kTests[i].encoded, kTests[i].enclen) == 0); + // Make sure remainder of buffer is unchanged + ASSERT(memory_check(buffer + kTests[i].enclen, + 0x1, + ARRAY_SIZE(buffer) - kTests[i].enclen)); + } + return 1; +} + +int test = utf8_unittest(); + +#endif // _DEBUG + +///////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/stringencode.h b/Plugins/jingle/libjingle/talk/base/stringencode.h new file mode 100644 index 0000000..08e5e4f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringencode.h @@ -0,0 +1,166 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_STRINGENCODE_H__ +#define TALK_BASE_STRINGENCODE_H__ + +#include <string> +#include <sstream> + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +////////////////////////////////////////////////////////////////////// + +// Convert an unsigned value from 0 to 15 to the hex character equivalent... +char hex_encode(unsigned char val); +// ...and vice-versa. +unsigned char hex_decode(char ch); + +// Convert an unsigned value to it's utf8 representation. Returns the length +// of the encoded string, or 0 if the encoding is longer than buflen - 1. +size_t utf8_encode(char* buffer, size_t buflen, unsigned long value); +// Decode the utf8 encoded value pointed to by source. Returns the number of +// bytes used by the encoding, or 0 if the encoding is invalid. +size_t utf8_decode(const char* source, size_t srclen, unsigned long* value); + +// Escaping prefixes illegal characters with the escape character. Compact, but +// illegal characters still appear in the string. +size_t escape(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape); +// Note: in-place unescaping (buffer == source) is allowed. +size_t unescape(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape); + +// Encoding replaces illegal characters with the escape character and 2 hex +// chars, so it's a little less compact than escape, but completely removes +// illegal characters. note that hex digits should not be used as illegal +// characters. +size_t encode(char * buffer, size_t buflen, + const char * source, size_t srclen, + const char * illegal, char escape); +// Note: in-place decoding (buffer == source) is allowed. +size_t decode(char * buffer, size_t buflen, + const char * source, size_t srclen, + char escape); + +// Returns a list of characters that may be unsafe for use in the name of a +// file, suitable for passing to the 'illegal' member of escape or encode. +const char* unsafe_filename_characters(); + +// url_encode is an encode operation with a predefined set of illegal characters +// and escape character (for use in URLs, obviously). +size_t url_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t url_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// html_encode prevents data embedded in html from containing markup. +size_t html_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t html_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// xml_encode makes data suitable for inside xml attributes and values. +size_t xml_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +// Note: in-place decoding (buffer == source) is allowed. +size_t xml_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// hex_encode shows the hex representation of binary data in ascii. +size_t hex_encode(char * buffer, size_t buflen, + const char * source, size_t srclen); +size_t hex_decode(char * buffer, size_t buflen, + const char * source, size_t srclen); + +// Apply any suitable string transform (including the ones above) to an STL +// string. Stack-allocated temporary space is used for the transformation, +// so value and source may refer to the same string. +typedef size_t (*Transform)(char * buffer, size_t buflen, + const char * source, size_t srclen); +void transform(std::string& value, size_t maxlen, const std::string& source, + Transform t); + +// Return the result of applying transform t to source. +std::string s_transform(const std::string& source, Transform t); + +// Convenience wrappers +inline std::string s_url_encode(const std::string& source) { + return s_transform(source, url_encode); +} +inline std::string s_url_decode(const std::string& source) { + return s_transform(source, url_decode); +} + +// Safe sprintf to std::string +//void sprintf(std::string& value, size_t maxlen, const char * format, ...) +// PRINTF_FORMAT(3); + +// Convert arbitrary values to/from a string. + +template <class T> +static bool ToString(const T &t, std::string* s) { + std::ostringstream oss; + oss << t; + *s = oss.str(); + return !oss.fail(); +} + +template <class T> +static bool FromString(const std::string& s, T* t) { + std::istringstream iss(s); + iss >> *t; + return !iss.fail(); +} + +// Inline versions of the string conversion routines. + +template<typename T> +static inline std::string ToString(T val) { + std::string str; ToString(val, &str); return str; +} + +template<typename T> +static inline T FromString(const std::string& str) { + T val; FromString(str, &val); return val; +} + +// simple function to strip out characters which shouldn't be +// used in filenames +char make_char_safe_for_filename(char c); + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_STRINGENCODE_H__ diff --git a/Plugins/jingle/libjingle/talk/base/stringutils.cc b/Plugins/jingle/libjingle/talk/base/stringutils.cc new file mode 100644 index 0000000..1e43637 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringutils.cc @@ -0,0 +1,84 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/stringutils.h" +#include "talk/base/common.h" + +namespace talk_base { + +bool memory_check(const void* memory, int c, size_t count) { + const char* char_memory = static_cast<const char*>(memory); + char char_c = static_cast<char>(c); + for (size_t i=0; i<count; ++i) { + if (char_memory[i] != char_c) { + return false; + } + } + return true; +} + +#ifdef WIN32 +int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, + CharacterTransformation transformation) { + wchar_t c1, c2; + while (true) { + if (n-- == 0) return 0; + c1 = transformation(*s1); + // Double check that characters are not UTF-8 + ASSERT(static_cast<unsigned char>(*s2) < 128); + // Note: *s2 gets implicitly promoted to wchar_t + c2 = transformation(*s2); + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (!c1) return 0; + ++s1; + ++s2; + } +} + +size_t asccpyn(wchar_t* buffer, size_t buflen, + const char* source, size_t srclen) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlenn(source, buflen - 1); + } else if (srclen >= buflen) { + srclen = buflen - 1; + } +#if _DEBUG + // Double check that characters are not UTF-8 + for (size_t pos = 0; pos < srclen; ++pos) + ASSERT(static_cast<unsigned char>(source[pos]) < 128); +#endif // _DEBUG + std::copy(source, source + srclen, buffer); + buffer[srclen] = 0; + return srclen; +} + +#endif // WIN32 + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/stringutils.h b/Plugins/jingle/libjingle/talk/base/stringutils.h new file mode 100644 index 0000000..c3051fd --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/stringutils.h @@ -0,0 +1,291 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_STRINGUTILS_H__ +#define TALK_BASE_STRINGUTILS_H__ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#ifdef WIN32 +#include <wchar.h> +#endif // WIN32 + +#include <string> + +/////////////////////////////////////////////////////////////////////////////// +// Generic string/memory utilities +/////////////////////////////////////////////////////////////////////////////// + +namespace talk_base { + +// Complement to memset. Verifies memory consists of count bytes of value c. +bool memory_check(const void* memory, int c, size_t count); + +} // namespace talk_base + +/////////////////////////////////////////////////////////////////////////////// +// Rename a bunch of common string functions so they are consistent across +// platforms and between char and wchar_t variants. +// Here is the full list of functions that are unified: +// strlen, strcmp, stricmp, strncmp, strnicmp +// strchr, vsnprintf, strtoul, tolowercase +// tolowercase is like tolower, but not compatible with end-of-file value +// Note that the wchar_t versions are not available on Linux +/////////////////////////////////////////////////////////////////////////////// + +inline char tolowercase(char c) { + return static_cast<char>(tolower(c)); +} + +#ifdef WIN32 + +inline size_t strlen(const wchar_t* s) { + return wcslen(s); +} +inline int strcmp(const wchar_t* s1, const wchar_t* s2) { + return wcscmp(s1, s2); +} +inline int stricmp(const wchar_t* s1, const wchar_t* s2) { + return _wcsicmp(s1, s2); +} +inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return wcsncmp(s1, s2, n); +} +inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) { + return _wcsnicmp(s1, s2, n); +} +inline const wchar_t* strchr(const wchar_t* s, wchar_t c) { + return wcschr(s, c); +} +inline const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) { + return wcsstr(haystack, needle); +} +inline int vsnprintf(char* buf, size_t n, const char* fmt, va_list args) { + return _vsnprintf(buf, n, fmt, args); +} +inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) { + return _vsnwprintf(buf, n, fmt, args); +} +inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) { + return wcstoul(snum, end, base); +} +inline wchar_t tolowercase(wchar_t c) { + return static_cast<wchar_t>(towlower(c)); +} + +#endif // WIN32 + +#ifdef POSIX + +inline int _stricmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline int _strnicmp(const char* s1, const char* s2, size_t n) { + return strncasecmp(s1, s2, n); +} + +#endif // POSIX + +/////////////////////////////////////////////////////////////////////////////// +// Traits simplifies porting string functions to be CTYPE-agnostic +/////////////////////////////////////////////////////////////////////////////// + +namespace talk_base { + +const size_t SIZE_UNKNOWN = static_cast<size_t>(-1); + +template<class CTYPE> +struct Traits { + // STL string type + //typedef XXX string; + // Null-terminated string + //inline static const CTYPE* empty_str(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// String utilities which work with char or wchar_t +/////////////////////////////////////////////////////////////////////////////// + +template<class CTYPE> +inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) { + return str ? str : (def_str ? def_str : Traits<CTYPE>::empty_str()); +} + +template<class CTYPE> +const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) { + for (size_t i=0; str[i]; ++i) { + for (size_t j=0; chs[j]; ++j) { + if (str[i] == chs[j]) { + return str + i; + } + } + } + return 0; +} + +template<class CTYPE> +const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) { + for (size_t i=0; i<slen && str[i]; ++i) { + if (str[i] == ch) { + return str + i; + } + } + return 0; +} + +template<class CTYPE> +size_t strlenn(const CTYPE* buffer, size_t buflen) { + size_t bufpos = 0; + while (buffer[bufpos] && (bufpos < buflen)) { + ++bufpos; + } + return bufpos; +} + +// Safe versions of strncpy, strncat, snprintf and vsnprintf that always +// null-terminate. + +template<class CTYPE> +size_t strcpyn(CTYPE* buffer, size_t buflen, + const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlenn(source, buflen - 1); + } else if (srclen >= buflen) { + srclen = buflen - 1; + } + memcpy(buffer, source, srclen * sizeof(CTYPE)); + buffer[srclen] = 0; + return srclen; +} + +template<class CTYPE> +size_t strcatn(CTYPE* buffer, size_t buflen, + const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { + if (buflen <= 0) + return 0; + + size_t bufpos = strlenn(buffer, buflen - 1); + return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen); +} + +template<class CTYPE> +size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) { + va_list args; + va_start(args, format); + size_t len = vsprintfn(buffer, buflen, format, args); + va_end(args); + return len; +} + +template<class CTYPE> +size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, + va_list args) { + int len = vsnprintf(buffer, buflen, format, args); + if ((len < 0) || (static_cast<size_t>(len) >= buflen)) { + len = static_cast<int>(buflen - 1); + buffer[len] = 0; + } + return len; +} + +/////////////////////////////////////////////////////////////////////////////// +// Allow safe comparing and copying ascii (not UTF-8) with both wide and +// non-wide character strings. +/////////////////////////////////////////////////////////////////////////////// + +inline int asccmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} +inline int ascicmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline int ascncmp(const char* s1, const char* s2, size_t n) { + return strncmp(s1, s2, n); +} +inline int ascnicmp(const char* s1, const char* s2, size_t n) { + return _strnicmp(s1, s2, n); +} +inline size_t asccpyn(char* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN) { + return strcpyn(buffer, buflen, source, srclen); +} + +#ifdef WIN32 + +typedef wchar_t(*CharacterTransformation)(wchar_t); +inline wchar_t identity(wchar_t c) { return c; } +int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, + CharacterTransformation transformation); + +inline int asccmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast<size_t>(-1), identity); +} +inline int ascicmp(const wchar_t* s1, const char* s2) { + return ascii_string_compare(s1, s2, static_cast<size_t>(-1), tolowercase); +} +inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, identity); +} +inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) { + return ascii_string_compare(s1, s2, n, tolowercase); +} +size_t asccpyn(wchar_t* buffer, size_t buflen, + const char* source, size_t srclen = SIZE_UNKNOWN); + +#endif // WIN32 + +/////////////////////////////////////////////////////////////////////////////// +// Traits<char> specializations +/////////////////////////////////////////////////////////////////////////////// + +template<> +struct Traits<char> { + typedef std::string string; + inline static const char* Traits<char>::empty_str() { return ""; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// Traits<wchar_t> specializations (Windows only, currently) +/////////////////////////////////////////////////////////////////////////////// + +#ifdef WIN32 + +template<> +struct Traits<wchar_t> { + typedef std::wstring string; + inline static const wchar_t* Traits<wchar_t>::empty_str() { return L""; } +}; + +#endif // WIN32 + +} // namespace talk_base + +#endif // TALK_BASE_STRINGUTILS_H__ diff --git a/Plugins/jingle/libjingle/talk/base/tarstream.cc b/Plugins/jingle/libjingle/talk/base/tarstream.cc new file mode 100644 index 0000000..e1f17b1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/tarstream.cc @@ -0,0 +1,601 @@ +#include "talk/base/basicdefs.h" +#include "talk/base/basictypes.h" +#include "talk/base/tarstream.h" +#include "talk/base/pathutils.h" +#include "talk/base/stringutils.h" +#include "talk/base/common.h" + +using namespace talk_base; + +/////////////////////////////////////////////////////////////////////////////// +// TarStream +/////////////////////////////////////////////////////////////////////////////// + +TarStream::TarStream() : mode_(M_NONE), next_block_(NB_NONE), block_pos_(0), + current_(NULL), current_bytes_(0) { +} + +TarStream::~TarStream() { + Close(); +} + +bool TarStream::AddFilter(const std::string& pathname) { + if (pathname.empty()) + return false; + Pathname archive_path(pathname); + archive_path.SetFolderDelimiter('/'); + archive_path.Normalize(); + filters_.push_back(archive_path.pathname()); + return true; +} + +bool TarStream::Open(const std::string& folder, bool read) { + Close(); + + Pathname root_folder; + root_folder.SetFolder(folder); + root_folder.Normalize(); + root_folder_.assign(root_folder.folder()); + + if (read) { + std::string pattern(root_folder_); + DirectoryIterator *iter = new DirectoryIterator(); + + if (iter->Iterate(pattern) == false) { + delete iter; + return false; + } + mode_ = M_READ; + find_.push_front(iter); + next_block_ = NB_FILE_HEADER; + block_pos_ = BLOCK_SIZE; + int error; + if (SR_SUCCESS != ProcessNextEntry(find_.front(), &error)) { + return false; + } + } else { + if (!Filesystem::CreateFolder(root_folder_)) { + return false; + } + + mode_ = M_WRITE; + next_block_ = NB_FILE_HEADER; + block_pos_ = 0; + } + return true; +} + +StreamState TarStream::GetState() const { + return (M_NONE == mode_) ? SS_CLOSED : SS_OPEN; +} + +StreamResult TarStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (M_READ != mode_) { + return SR_EOS; + } + return ProcessBuffer(buffer, buffer_len, read, error); +} + +StreamResult TarStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (M_WRITE != mode_) { + return SR_EOS; + } + // Note: data is not modified unless M_READ == mode_ + return ProcessBuffer(const_cast<void*>(data), data_len, written, error); +} + +void TarStream::Close() { + root_folder_.clear(); + next_block_ = NB_NONE; + block_pos_ = 0; + delete current_; + current_ = NULL; + current_bytes_ = 0; + for (DirectoryList::iterator it = find_.begin(); it != find_.end(); ++it) { + delete(*it); + } + find_.clear(); + subfolder_.clear(); +} + +StreamResult TarStream::ProcessBuffer(void* buffer, size_t buffer_len, + size_t* consumed, int* error) { + size_t local_consumed; + if (!consumed) consumed = &local_consumed; + int local_error; + if (!error) error = &local_error; + + StreamResult result = SR_SUCCESS; + *consumed = 0; + + while (*consumed < buffer_len) { + size_t available = BLOCK_SIZE - block_pos_; + if (available == 0) { + result = ProcessNextBlock(error); + if (SR_SUCCESS != result) { + break; + } + } else { + size_t bytes_to_copy = talk_base::_min(available, buffer_len - *consumed); + char* buffer_ptr = static_cast<char*>(buffer) + *consumed; + char* block_ptr = block_ + block_pos_; + if (M_READ == mode_) { + memcpy(buffer_ptr, block_ptr, bytes_to_copy); + } else { + memcpy(block_ptr, buffer_ptr, bytes_to_copy); + } + *consumed += bytes_to_copy; + block_pos_ += bytes_to_copy; + } + } + + // SR_EOS means no data was consumed on this operation. So we may need to + // return SR_SUCCESS instead, and then we will return SR_EOS next time. + if ((SR_EOS == result) && (*consumed > 0)) { + result = SR_SUCCESS; + } + + return result; +} + +StreamResult TarStream::ProcessNextBlock(int* error) { + ASSERT(NULL != error); + ASSERT(M_NONE != mode_); + ASSERT(BLOCK_SIZE == block_pos_); + + StreamResult result; + if (NB_NONE == next_block_) { + + return SR_EOS; + + } else if (NB_TRAILER == next_block_) { + + // Trailer block is zeroed + result = ProcessEmptyBlock(0, error); + if (SR_SUCCESS != result) + return result; + next_block_ = NB_NONE; + + } else if (NB_FILE_HEADER == next_block_) { + + if (M_READ == mode_) { + result = ReadNextFile(error); + } else { + result = WriteNextFile(error); + } + + // If there are no more files, we are at the first trailer block + if (SR_EOS == result) { + block_pos_ = 0; + next_block_ = NB_TRAILER; + result = ProcessEmptyBlock(0, error); + } + if (SR_SUCCESS != result) + return result; + + } else if (NB_DATA == next_block_) { + + size_t block_consumed = 0; + size_t block_available = talk_base::_min<size_t>(BLOCK_SIZE, current_bytes_); + while (block_consumed < block_available) { + void* block_ptr = static_cast<char*>(block_) + block_consumed; + size_t available = block_available - block_consumed, consumed; + if (M_READ == mode_) { + ASSERT(NULL != current_); + result = current_->Read(block_ptr, available, &consumed, error); + } else if (current_) { + result = current_->Write(block_ptr, available, &consumed, error); + } else { + consumed = available; + result = SR_SUCCESS; + } + switch (result) { + case SR_ERROR: + return result; + case SR_BLOCK: + case SR_EOS: + ASSERT(false); + *error = 0; // TODO: make errors + return SR_ERROR; + case SR_SUCCESS: + block_consumed += consumed; + break; + } + } + + current_bytes_ -= block_consumed; + if (current_bytes_ == 0) { + // The remainder of the block is zeroed + result = ProcessEmptyBlock(block_consumed, error); + if (SR_SUCCESS != result) + return result; + delete current_; + current_ = NULL; + next_block_ = NB_FILE_HEADER; + } + + } else { + ASSERT(false); + } + + block_pos_ = 0; + return SR_SUCCESS; +} + +StreamResult TarStream::ProcessEmptyBlock(size_t start, int* error) { + ASSERT(NULL != error); + ASSERT(M_NONE != mode_); + if (M_READ == mode_) { + memset(block_ + start, 0, BLOCK_SIZE - start); + } else { + if (!talk_base::memory_check(block_ + start, 0, BLOCK_SIZE - start)) { + *error = 0; // TODO: make errors + return SR_ERROR; + } + } + return SR_SUCCESS; +} + +StreamResult TarStream::ReadNextFile(int* error) { + ASSERT(NULL != error); + ASSERT(M_READ == mode_); + ASSERT(NB_FILE_HEADER == next_block_); + ASSERT(BLOCK_SIZE == block_pos_); + ASSERT(NULL == current_); + + // ReadNextFile conducts a depth-first recursive search through the directory + // tree. find_ maintains a stack of open directory handles, which + // corresponds to our current position in the tree. At any point, the + // directory at the top (front) of the stack is being enumerated. If a + // directory is found, it is opened and pushed onto the top of the stack. + // When a directory enumeration completes, that directory is popped off the + // top of the stack. + + // Note: Since ReadNextFile can only return one block of data at a time, we + // cannot simultaneously return the entry for a directory, and the entry for + // the first element in that directory at the same time. In this case, we + // push a NULL entry onto the find_ stack, which indicates that the next + // iteration should begin enumeration of the "new" directory. + StreamResult result = SR_SUCCESS; + while (BLOCK_SIZE == block_pos_) { + ASSERT(!find_.empty()); + + if (NULL != find_.front()) { + if (find_.front()->Next()) { + result = ProcessNextEntry(find_.front(), error); + if (SR_SUCCESS != result) { + return result; + } + continue; + } + delete(find_.front()); + } else { + Pathname pattern(root_folder_); + pattern.AppendFolder(subfolder_); + find_.front() = new DirectoryIterator(); + if (find_.front()->Iterate(pattern.pathname())) { + result = ProcessNextEntry(find_.front(), error); + if (SR_SUCCESS != result) { + return result; + } + continue; + } + // TODO: should this be an error? + LOG_F(LS_WARNING) << "Couldn't open folder: " << pattern.pathname(); + } + + find_.pop_front(); + subfolder_ = Pathname(subfolder_).parent_folder(); + + if (find_.empty()) { + return SR_EOS; + } + } + + ASSERT(0 == block_pos_); + return SR_SUCCESS; +} + +StreamResult TarStream::WriteNextFile(int* error) { + ASSERT(NULL != error); + ASSERT(M_WRITE == mode_); + ASSERT(NB_FILE_HEADER == next_block_); + ASSERT(BLOCK_SIZE == block_pos_); + ASSERT(NULL == current_); + ASSERT(0 == current_bytes_); + + std::string pathname, link, linked_name, magic, mversion; + size_t file_size, modify_time, unused, checksum; + + size_t block_data = 0; + ReadFieldS(block_data, 100, &pathname); + ReadFieldN(block_data, 8, &unused); // mode + ReadFieldN(block_data, 8, &unused); // owner uid + ReadFieldN(block_data, 8, &unused); // owner gid + ReadFieldN(block_data, 12, &file_size); + ReadFieldN(block_data, 12, &modify_time); + ReadFieldN(block_data, 8, &checksum); + if (checksum == 0) + block_data -= 8; // back-compatiblity + ReadFieldS(block_data, 1, &link); + ReadFieldS(block_data, 100, &linked_name); // name of linked file + ReadFieldS(block_data, 6, &magic); + ReadFieldS(block_data, 2, &mversion); + + if (pathname.empty()) + return SR_EOS; + + std::string user, group, dev_major, dev_minor, prefix; + if (magic == "ustar" || magic == "ustar ") { + ReadFieldS(block_data, 32, &user); + ReadFieldS(block_data, 32, &group); + ReadFieldS(block_data, 8, &dev_major); + ReadFieldS(block_data, 8, &dev_minor); + ReadFieldS(block_data, 155, &prefix); + + pathname = prefix + pathname; + } + + // Rest of the block must be empty + StreamResult result = ProcessEmptyBlock(block_data, error); + if (SR_SUCCESS != result) { + return result; + } + + Pathname archive_path(pathname); + archive_path.SetFolderDelimiter('/'); + archive_path.Normalize(); + + bool is_folder = archive_path.filename().empty(); + if (is_folder) { + ASSERT(NB_FILE_HEADER == next_block_); + ASSERT(0 == file_size); + } else if (file_size > 0) { + // We assign current_bytes_ because we must skip over the upcoming data + // segments, regardless of whether we want to write them. + next_block_ = NB_DATA; + current_bytes_ = file_size; + } + + if (!CheckFilter(archive_path.pathname())) { + // If it's a directory, we will ignore it and all children by nature of + // filter prefix matching. If it is a file, we will ignore it because + // current_ is NULL. + return SR_SUCCESS; + } + + // Sanity checks: + // 1) No .. path segments + if (archive_path.pathname().find("../") != std::string::npos) { + LOG_F(LS_WARNING) << "Skipping path with .. entry: " + << archive_path.pathname(); + return SR_SUCCESS; + } + // 2) No drive letters + if (archive_path.pathname().find(':') != std::string::npos) { + LOG_F(LS_WARNING) << "Skipping path with drive letter: " + << archive_path.pathname(); + return SR_SUCCESS; + } + // 3) No absolute paths + if (archive_path.pathname().find("//") != std::string::npos) { + LOG_F(LS_WARNING) << "Skipping absolute path: " + << archive_path.pathname(); + return SR_SUCCESS; + } + + Pathname local_path(root_folder_); + local_path.AppendPathname(archive_path.pathname()); + local_path.Normalize(); + + if (is_folder) { + if (!Filesystem::CreateFolder(local_path)) { + LOG_F(LS_WARNING) << "Couldn't create folder: " << local_path.pathname(); + *error = 0; // TODO + return SR_ERROR; + } + } else { + FileStream* stream = new FileStream; + + if (!stream->Open(local_path.pathname().c_str(), "wb")) { + LOG_F(LS_WARNING) << "Couldn't create file: " << local_path.pathname(); + *error = 0; // TODO + delete stream; + return SR_ERROR; + } + if (file_size > 0) { + current_ = stream; + } else { + stream->Close(); + delete stream; + } + } + + SignalNextEntry(archive_path.filename(), current_bytes_); + + + return SR_SUCCESS; +} + +StreamResult TarStream::ProcessNextEntry(const DirectoryIterator *data, int *error) { + ASSERT(M_READ == mode_); + ASSERT(NB_FILE_HEADER == next_block_); + ASSERT(BLOCK_SIZE == block_pos_); + ASSERT(NULL == current_); + ASSERT(0 == current_bytes_); + + if (data->IsDirectory() && + (data->Name() == "." || data->Name() == "..")) + return SR_SUCCESS; + + Pathname archive_path; + archive_path.SetFolder(subfolder_); + if (data->IsDirectory()) { + archive_path.AppendFolder(data->Name()); + } else { + archive_path.SetFilename(data->Name()); + } + archive_path.SetFolderDelimiter('/'); + archive_path.Normalize(); + + if (!CheckFilter(archive_path.pathname())) + return SR_SUCCESS; + + if (archive_path.pathname().length() > 255) { + // Cannot send a file name longer than 255 (yet) + return SR_ERROR; + } + + Pathname local_path(root_folder_); + local_path.AppendPathname(archive_path.pathname()); + local_path.Normalize(); + + if (data->IsDirectory()) { + // Note: the NULL handle indicates that we need to open the folder next + // time. + find_.push_front(NULL); + subfolder_ = archive_path.pathname(); + } else { + FileStream* stream = new FileStream; + if (!stream->Open(local_path.pathname().c_str(), "rb")) { + // TODO: Should this be an error? + LOG_F(LS_WARNING) << "Couldn't open file: " << local_path.pathname(); + delete stream; + return SR_SUCCESS; + } + current_ = stream; + current_bytes_ = data->FileSize(); + } + + time_t modify_time = data->FileModifyTime(); + + std::string pathname = archive_path.pathname(); + std::string magic, user, group, dev_major, dev_minor, prefix; + std::string name = pathname; + bool ustar = false; + if (name.length() > 100) { + ustar = true; + // Put last 100 characters into the name, and rest in prefix + size_t path_length = pathname.length(); + prefix = pathname.substr(0, path_length - 100); + name = pathname.substr(path_length - 100); + } + + size_t block_data = 0; + memset(block_, 0, BLOCK_SIZE); + WriteFieldS(block_data, 100, name.c_str()); + WriteFieldS(block_data, 8, data->IsDirectory() ? "777" : "666"); // mode + WriteFieldS(block_data, 8, "5"); // owner uid + WriteFieldS(block_data, 8, "5"); // owner gid + WriteFieldN(block_data, 12, current_bytes_); + WriteFieldN(block_data, 12, modify_time); + WriteFieldS(block_data, 8, " "); // Checksum. To be filled in later. + WriteFieldS(block_data, 1, data->IsDirectory() ? "5" : "0"); // link indicator (0 == normal file, 5 == directory) + WriteFieldS(block_data, 100, ""); // name of linked file + + if (ustar) { + WriteFieldS(block_data, 6, "ustar"); + WriteFieldS(block_data, 2, ""); + WriteFieldS(block_data, 32, user.c_str()); + WriteFieldS(block_data, 32, group.c_str()); + WriteFieldS(block_data, 8, dev_major.c_str()); + WriteFieldS(block_data, 8, dev_minor.c_str()); + WriteFieldS(block_data, 155, prefix.c_str()); + } + + // Rest of the block must be empty + StreamResult result = ProcessEmptyBlock(block_data, error); + WriteChecksum(); + + block_pos_ = 0; + if (current_bytes_ > 0) { + next_block_ = data->IsDirectory() ? NB_FILE_HEADER : NB_DATA; + } + + SignalNextEntry(archive_path.filename(), current_bytes_); + + return result; +} + +void TarStream::WriteChecksum() { + unsigned int sum = 0; + + for (int i = 0; i < BLOCK_SIZE; i++) + sum += static_cast<unsigned char>(block_[i]); + + sprintf(block_ + 148, "%06o", sum); +} + +bool TarStream::CheckFilter(const std::string& pathname) { + if (filters_.empty()) + return true; + + // pathname is allowed when there is a filter which: + // A) Equals name + // B) Matches a folder prefix of name + for (size_t i=0; i<filters_.size(); ++i) { + const std::string& filter = filters_[i]; + // Make sure the filter is a prefix of name + if (_strnicmp(pathname.c_str(), filter.data(), filter.length()) != 0) + continue; + + // If the filter is not a directory, must match exactly + if (!Pathname::IsFolderDelimiter(filter[filter.length()-1]) + && (filter.length() != pathname.length())) + continue; + + return true; + } + + return false; +} + +void TarStream::WriteFieldN(size_t& pos, size_t max_len, size_t numeric_field) { + WriteFieldF(pos, max_len, "%.*o", max_len - 1, numeric_field); +} + +void TarStream::WriteFieldS(size_t& pos, size_t max_len, + const char* string_field) { + ASSERT(pos + max_len <= BLOCK_SIZE); + size_t len = strlen(string_field); + size_t use_len = _min(len, max_len); + memcpy(block_ + pos, string_field, use_len); + pos += max_len; +} + +void TarStream::WriteFieldF(size_t& pos, size_t max_len, + const char* format, ...) { + va_list args; + va_start(args, format); + char buffer[BLOCK_SIZE]; + vsprintfn(buffer, ARRAY_SIZE(buffer), format, args); + WriteFieldS(pos, max_len, buffer); + va_end(args); +} + +void TarStream::ReadFieldN(size_t& pos, size_t max_len, size_t* numeric_field) { + ASSERT(NULL != numeric_field); + std::string buffer; + ReadFieldS(pos, max_len, &buffer); + + int value; + if (!buffer.empty() && (1 == sscanf(buffer.c_str(), "%o", &value))) { + *numeric_field = value; + } else { + *numeric_field = 0; + } +} + +void TarStream::ReadFieldS(size_t& pos, size_t max_len, + std::string* string_field) { + ASSERT(NULL != string_field); + ASSERT(pos + max_len <= BLOCK_SIZE); + size_t value_len = talk_base::strlenn(block_ + pos, max_len); + string_field->assign(block_ + pos, value_len); + ASSERT(talk_base::memory_check(block_ + pos + value_len, + 0, + max_len - value_len)); + pos += max_len; +} diff --git a/Plugins/jingle/libjingle/talk/base/tarstream.h b/Plugins/jingle/libjingle/talk/base/tarstream.h new file mode 100644 index 0000000..772fb14 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/tarstream.h @@ -0,0 +1,104 @@ +#ifndef TALK_APP_WIN32_TARSTREAM_H__ +#define TALK_APP_WIN32_TARSTREAM_H__ + +#include <string> +#include <vector> +#include "talk/base/fileutils.h" +#include "talk/base/sigslot.h" +#include "talk/base/stream.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// TarStream - acts as a source or sink for a tar-encoded collection of files +// and directories. Operates synchronously. +/////////////////////////////////////////////////////////////////////////////// + +class TarStream : public StreamInterface { + public: + TarStream(); + virtual ~TarStream(); + + // AddFilter is used to limit the elements which will be read or written. + // In general, all members of the parent folder are read, and all members + // of a tarfile are written. However, if any filters are added, only those + // items (and their contents, in the case of folders) are processed. Filters + // must be added before opening the stream. + bool AddFilter(const std::string& pathname); + + // 'folder' is parent of the tar contents. All paths will be evaluated + // relative to it. When 'read' is true, the specified folder will be + // traversed, and a tar stream will be generated (via Read). Otherwise, a + // tar stream is consumed (via Write), and files and folders will be created. + bool Open(const std::string& folder, bool read); + + virtual talk_base::StreamState GetState() const; + virtual talk_base::StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual talk_base::StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + + virtual bool GetSize(size_t* size) const { return false; } + virtual bool ReserveSize(size_t size) { return true; } + virtual bool Rewind() { return false; } + + // Every time a new entry header is read/written, this signal is fired with + // the entry's name and size. + sigslot::signal2<const std::string&, size_t> SignalNextEntry; + + private: + typedef std::list<DirectoryIterator*> DirectoryList; + enum ModeType { M_NONE, M_READ, M_WRITE }; + enum NextBlockType { NB_NONE, NB_FILE_HEADER, NB_DATA, NB_TRAILER }; + enum { BLOCK_SIZE = 512 }; + + talk_base::StreamResult ProcessBuffer(void* buffer, size_t buffer_len, + size_t* consumed, int* error); + talk_base::StreamResult ProcessNextBlock(int* error); + talk_base::StreamResult ProcessEmptyBlock(size_t start, int* error); + talk_base::StreamResult ReadNextFile(int* error); + talk_base::StreamResult WriteNextFile(int* error); + + talk_base::StreamResult ProcessNextEntry(const DirectoryIterator *data, + int *error); + + // Determine whether the given entry is allowed by our filters + bool CheckFilter(const std::string& pathname); + + void WriteFieldN(size_t& pos, size_t max_len, size_t numeric_field); + void WriteFieldS(size_t& pos, size_t max_len, const char* string_field); + void WriteFieldF(size_t& pos, size_t max_len, const char* format, ...); + + void ReadFieldN(size_t& pos, size_t max_len, size_t* numeric_field); + void ReadFieldS(size_t& pos, size_t max_len, std::string* string_field); + + void WriteChecksum(void); + + // Files and/or folders that should be processed + std::vector<std::string> filters_; + // Folder passed to Open + std::string root_folder_; + // Open for read or write? + ModeType mode_; + // The expected type of the next block + NextBlockType next_block_; + // The partial contents of the current block + char block_[BLOCK_SIZE]; + size_t block_pos_; + // The file which is currently being read or written + talk_base::FileStream* current_; + // Bytes remaining to be processed for current_ + size_t current_bytes_; + // Note: the following variables are used in M_READ mode only. + // Stack of open directory handles, representing depth-first search + DirectoryList find_; + // Subfolder path corresponding to current position in the directory tree + std::string subfolder_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_APP_WIN32_TARSTREAM_H__ diff --git a/Plugins/jingle/libjingle/talk/base/task.cc b/Plugins/jingle/libjingle/talk/base/task.cc new file mode 100644 index 0000000..d7e0e82 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/task.cc @@ -0,0 +1,299 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> + +#include "talk/base/task.h" +#include "talk/base/common.h" +#include "talk/base/taskrunner.h" + +namespace talk_base { + +int32 Task::unique_id_seed_ = 0; + +Task::Task(Task *parent) + : state_(STATE_INIT), + parent_(parent), + blocked_(false), + done_(false), + aborted_(false), + busy_(false), + error_(false), + child_error_(false), + start_time_(0), + timeout_seconds_(0), + timeout_time_(0), + timeout_suspended_(false) { + children_.reset(new ChildSet()); + runner_ = ((parent == NULL) ? + reinterpret_cast<TaskRunner *>(this) : + parent->GetRunner()); + if (parent_ != NULL) { + parent_->AddChild(this); + } + + unique_id_ = unique_id_seed_++; + + // sanity check that we didn't roll-over our id seed + ASSERT(unique_id_ < unique_id_seed_); +} + +int64 Task::CurrentTime() { + return runner_->CurrentTime(); +} + +int64 Task::ElapsedTime() { + return CurrentTime() - start_time_; +} + +void Task::Start() { + if (state_ != STATE_INIT) + return; + // Set the start time before starting the task. Otherwise if the task + // finishes quickly and deletes the Task object, setting start_time_ + // will crash. + start_time_ = CurrentTime(); + GetRunner()->StartTask(this); +} + +void Task::Step() { + if (done_) { +#ifdef DEBUG + // we do not know how !blocked_ happens when done_ - should be impossible. + // But it causes problems, so in retail build, we force blocked_, and + // under debug we assert. + assert(blocked_); +#else + blocked_ = true; +#endif + return; + } + + // Async Error() was called + if (error_) { + done_ = true; + state_ = STATE_ERROR; + blocked_ = true; +// obsolete - an errored task is not considered done now +// SignalDone(); + Stop(); + return; + } + + busy_ = true; + int new_state = Process(state_); + busy_ = false; + + if (aborted_) { + Abort(true); // no need to wake because we're awake + return; + } + + if (new_state == STATE_BLOCKED) { + blocked_ = true; + // Let the timeout continue + } else { + state_ = new_state; + blocked_ = false; + ResetTimeout(); + } + + if (new_state == STATE_DONE) { + done_ = true; + } else if (new_state == STATE_ERROR) { + done_ = true; + error_ = true; + } + + if (done_) { +// obsolete - call this yourself +// SignalDone(); + Stop(); + blocked_ = true; + } +} + +void Task::Abort(bool nowake) { + if (aborted_ || done_) + return; + aborted_ = true; + if (!busy_) { + done_ = true; + blocked_ = true; + error_ = true; + Stop(); + if (!nowake) + Wake(); // to self-delete + } +} + +void Task::Wake() { + if (done_) + return; + if (blocked_) { + blocked_ = false; + GetRunner()->WakeTasks(); + } +} + +void Task::Error() { + if (error_ || done_) + return; + error_ = true; + Wake(); +} + +std::string Task::GetStateName(int state) const { + static const std::string STR_BLOCKED("BLOCKED"); + static const std::string STR_INIT("INIT"); + static const std::string STR_START("START"); + static const std::string STR_DONE("DONE"); + static const std::string STR_ERROR("ERROR"); + static const std::string STR_RESPONSE("RESPONSE"); + static const std::string STR_HUH("??"); + switch (state) { + case STATE_BLOCKED: return STR_BLOCKED; + case STATE_INIT: return STR_INIT; + case STATE_START: return STR_START; + case STATE_DONE: return STR_DONE; + case STATE_ERROR: return STR_ERROR; + case STATE_RESPONSE: return STR_RESPONSE; + } + return STR_HUH; +} + +int Task::Process(int state) { + int newstate = STATE_ERROR; + + if (TimedOut()) { + ClearTimeout(); + newstate = OnTimeout(); + SignalTimeout(); + } else { + switch (state) { + case STATE_INIT: + newstate = STATE_START; + break; + case STATE_START: + newstate = ProcessStart(); + break; + case STATE_RESPONSE: + newstate = ProcessResponse(); + break; + case STATE_DONE: + case STATE_ERROR: + newstate = STATE_BLOCKED; + break; + } + } + + return newstate; +} + +void Task::AddChild(Task *child) { + children_->insert(child); +} + +bool Task::AllChildrenDone() { + for (ChildSet::iterator it = children_->begin(); + it != children_->end(); + ++it) { + if (!(*it)->IsDone()) + return false; + } + return true; +} + +bool Task::AnyChildError() { + return child_error_; +} + +void Task::AbortAllChildren() { + if (children_->size() > 0) { + ChildSet copy = *children_; + for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) { + (*it)->Abort(true); // Note we do not wake + } + } +} + +void Task::Stop() { + // No need to wake because we're either awake or in abort + AbortAllChildren(); + parent_->OnChildStopped(this); +} + +void Task::OnChildStopped(Task *child) { + if (child->HasError()) + child_error_ = true; + children_->erase(child); +} + +void Task::set_timeout_seconds(const int timeout_seconds) { + timeout_seconds_ = timeout_seconds; + ResetTimeout(); +} + +bool Task::TimedOut() { + return timeout_seconds_ && + timeout_time_ && + CurrentTime() > timeout_time_; +} + +void Task::ResetTimeout() { + bool timeout_allowed = (state_ != STATE_INIT) + && (state_ != STATE_DONE) + && (state_ != STATE_ERROR); + if (timeout_seconds_ && timeout_allowed && !timeout_suspended_) + timeout_time_ = CurrentTime() + + (timeout_seconds_ * kSecToMsec * kMsecTo100ns); + else + timeout_time_ = 0; + + GetRunner()->UpdateTaskTimeout(this); +} + +void Task::ClearTimeout() { + timeout_time_ = 0; + GetRunner()->UpdateTaskTimeout(this); +} + +void Task::SuspendTimeout() { + if (!timeout_suspended_) { + timeout_suspended_ = true; + ResetTimeout(); + } +} + +void Task::ResumeTimeout() { + if (timeout_suspended_) { + timeout_suspended_ = false; + ResetTimeout(); + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/task.h b/Plugins/jingle/libjingle/talk/base/task.h new file mode 100644 index 0000000..b524ab7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/task.h @@ -0,0 +1,218 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_TASK_H__ +#define TALK_BASE_TASK_H__ + +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/base/basictypes.h" +#include "talk/base/sigslot.h" + +///////////////////////////////////////////////////////////////////// +// +// TASK +// +///////////////////////////////////////////////////////////////////// +// +// Task is a state machine infrastructure. States are pushed forward by +// pushing forwards a TaskRunner that holds on to all Tasks. The purpose +// of Task is threefold: +// +// (1) It manages ongoing work on the UI thread. Multitasking without +// threads, keeping it easy, keeping it real. :-) It does this by +// organizing a set of states for each task. When you return from your +// Process*() function, you return an integer for the next state. You do +// not go onto the next state yourself. Every time you enter a state, +// you check to see if you can do anything yet. If not, you return +// STATE_BLOCKED. If you _could_ do anything, do not return +// STATE_BLOCKED - even if you end up in the same state, return +// STATE_mysamestate. When you are done, return STATE_DONE and then the +// task will self-delete sometimea afterwards. +// +// (2) It helps you avoid all those reentrancy problems when you chain +// too many triggers on one thread. Basically if you want to tell a task +// to process something for you, you feed your task some information and +// then you Wake() it. Don't tell it to process it right away. If it +// might be working on something as you send it infomration, you may want +// to have a queue in the task. +// +// (3) Finally it helps manage parent tasks and children. If a parent +// task gets aborted, all the children tasks are too. The nice thing +// about this, for example, is if you have one parent task that +// represents, say, and Xmpp connection, then you can spawn a whole bunch +// of infinite lifetime child tasks and now worry about cleaning them up. +// When the parent task goes to STATE_DONE, the task engine will make +// sure all those children are aborted and get deleted. +// +// Notice that Task has a few built-in states, e.g., +// +// STATE_INIT - the task isn't running yet +// STATE_START - the task is in its first state +// STATE_RESPONSE - the task is in its second state +// STATE_DONE - the task is done +// +// STATE_ERROR - indicates an error - we should audit the error code in +// light of any usage of it to see if it should be improved. When I +// first put down the task stuff I didn't have a good sense of what was +// needed for Abort and Error, and now the subclasses of Task will ground +// the design in a stronger way. +// +// STATE_NEXT - the first undefined state number. (like WM_USER) - you +// can start defining more task states there. +// +// When you define more task states, just override Process(int state) and +// add your own switch statement. If you want to delegate to +// Task::Process, you can effectively delegate to its switch statement. +// No fancy method pointers or such - this is all just pretty low tech, +// easy to debug, and fast. +// +// Also notice that Task has some primitive built-in timeout functionality. +// +// A timeout is defined as "the task stays in STATE_BLOCKED longer than +// timeout_seconds_." +// +// Descendant classes can override this behavior by calling the +// various protected methods to change the timeout behavior. For +// instance, a descendand might call SuspendTimeout() when it knows +// that it isn't waiting for anything that might timeout, but isn't +// yet in the STATE_DONE state. +// + +namespace talk_base { + +class TaskRunner; + +// A task executes a sequence of steps + +class Task; +class RootTask; + +class Task { + public: + Task(Task *parent); + virtual ~Task() {} + + int32 get_unique_id() { return unique_id_; } + + void Start(); + void Step(); + int GetState() const { return state_; } + bool HasError() const { return (GetState() == STATE_ERROR); } + bool Blocked() const { return blocked_; } + bool IsDone() const { return done_; } + int64 ElapsedTime(); + + Task *GetParent() { return parent_; } + TaskRunner *GetRunner() { return runner_; } + virtual Task *GetParent(int code) { return parent_->GetParent(code); } + + // Called from outside to stop task without any more callbacks + void Abort(bool nowake = false); + + // For managing children + bool AllChildrenDone(); + bool AnyChildError(); + + bool TimedOut(); + + int64 get_timeout_time() { return timeout_time_; } + void set_timeout_seconds(int timeout_seconds); + + sigslot::signal0<> SignalTimeout; + + // Called inside the task to signal that the task may be unblocked + void Wake(); + + protected: + + enum { + STATE_BLOCKED = -1, + STATE_INIT = 0, + STATE_START = 1, + STATE_DONE = 2, + STATE_ERROR = 3, + STATE_RESPONSE = 4, + STATE_NEXT = 5, // Subclasses which need more states start here and higher + }; + + // Called inside to advise that the task should wake and signal an error + void Error(); + + int64 CurrentTime(); + + virtual std::string GetStateName(int state) const; + virtual int Process(int state); + virtual void Stop(); + virtual int ProcessStart() = 0; + virtual int ProcessResponse() { return STATE_DONE; } + + // for managing children (if any) + void AddChild(Task *child); + void AbortAllChildren(); + + void ResetTimeout(); + void ClearTimeout(); + + void SuspendTimeout(); + void ResumeTimeout(); + + protected: + virtual int OnTimeout() { + // by default, we are finished after timing out + return STATE_DONE; + } + + private: + void Done(); + void OnChildStopped(Task *child); + + int state_; + Task *parent_; + TaskRunner *runner_; + bool blocked_; + bool done_; + bool aborted_; + bool busy_; + bool error_; + bool child_error_; + int64 start_time_; + int64 timeout_time_; + int timeout_seconds_; + bool timeout_suspended_; + int32 unique_id_; + + static int32 unique_id_seed_; + + // for managing children + typedef std::set<Task *> ChildSet; + scoped_ptr<ChildSet> children_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_TASK_H__ diff --git a/Plugins/jingle/libjingle/talk/base/taskrunner.cc b/Plugins/jingle/libjingle/talk/base/taskrunner.cc new file mode 100644 index 0000000..050171f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/taskrunner.cc @@ -0,0 +1,176 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> + +#include "talk/base/taskrunner.h" + +#include "talk/base/common.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/task.h" +#include "talk/base/logging.h" + +namespace talk_base { + +TaskRunner::TaskRunner() + : Task(NULL), + tasks_running_(false), + next_timeout_task_(NULL) { +} + +TaskRunner::~TaskRunner() { + // this kills and deletes children silently! + AbortAllChildren(); + RunTasks(); +} + +void TaskRunner::StartTask(Task * task) { + tasks_.push_back(task); + + // the task we just started could be about to timeout -- + // make sure our "next timeout task" is correct + UpdateTaskTimeout(task); + + WakeTasks(); +} + +void TaskRunner::RunTasks() { + // Running continues until all tasks are Blocked (ok for a small # of tasks) + if (tasks_running_) { + return; // don't reenter + } + + tasks_running_ = true; + + int did_run = true; + while (did_run) { + did_run = false; + // use indexing instead of iterators because tasks_ may grow + for (size_t i = 0; i < tasks_.size(); ++i) { + while (!tasks_[i]->Blocked()) { + tasks_[i]->Step(); + did_run = true; + } + } + } + // Tasks are deleted when running has paused + bool need_timeout_recalc = false; + for (size_t i = 0; i < tasks_.size(); ++i) { + if (tasks_[i]->IsDone()) { + Task* task = tasks_[i]; + if (next_timeout_task_ && + task->get_unique_id() == next_timeout_task_->get_unique_id()) { + next_timeout_task_ = NULL; + need_timeout_recalc = true; + } + + delete task; + tasks_[i] = NULL; + } + } + // Finally, remove nulls + std::vector<Task *>::iterator it; + it = std::remove(tasks_.begin(), + tasks_.end(), + reinterpret_cast<Task *>(NULL)); + + tasks_.erase(it, tasks_.end()); + + if (need_timeout_recalc) + RecalcNextTimeout(NULL); + + tasks_running_ = false; +} + +void TaskRunner::PollTasks() { + // see if our "next potentially timed-out task" has indeed timed out. + // If it has, wake it up, then queue up the next task in line + if (next_timeout_task_ && + next_timeout_task_->TimedOut()) { + next_timeout_task_->Wake(); + WakeTasks(); + } +} + +// this function gets called frequently -- when each task changes +// state to something other than DONE, ERROR or BLOCKED, it calls +// ResetTimeout(), which will call this function to make sure that +// the next timeout-able task hasn't changed. The logic in this function +// prevents RecalcNextTimeout() from getting called in most cases, +// effectively making the task scheduler O-1 instead of O-N + +void TaskRunner::UpdateTaskTimeout(Task *task) { + ASSERT(task != NULL); + + // if the relevant task has a timeout, then + // check to see if it's closer than the current + // "about to timeout" task + if (task->get_timeout_time()) { + if (next_timeout_task_ == NULL || + (task->get_timeout_time() <= + next_timeout_task_->get_timeout_time())) { + next_timeout_task_ = task; + } + } else if (next_timeout_task_ != NULL && + task->get_unique_id() == next_timeout_task_->get_unique_id()) { + // otherwise, if the task doesn't have a timeout, + // and it used to be our "about to timeout" task, + // walk through all the tasks looking for the real + // "about to timeout" task + RecalcNextTimeout(task); + } +} + +void TaskRunner::RecalcNextTimeout(Task *exclude_task) { + // walk through all the tasks looking for the one + // which satisfies the following: + // it's not finished already + // we're not excluding it + // it has the closest timeout time + + int64 next_timeout_time = 0; + next_timeout_task_ = NULL; + + for (size_t i = 0; i < tasks_.size(); ++i) { + Task *task = tasks_[i]; + // if the task isn't complete, and it actually has a timeout time + if (!task->IsDone() && + (task->get_timeout_time() > 0)) + // if it doesn't match our "exclude" task + if (exclude_task == NULL || + exclude_task->get_unique_id() != task->get_unique_id()) + // if its timeout time is sooner than our current timeout time + if (next_timeout_time == 0 || + task->get_timeout_time() <= next_timeout_time) { + // set this task as our next-to-timeout + next_timeout_time = task->get_timeout_time(); + next_timeout_task_ = task; + } + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/taskrunner.h b/Plugins/jingle/libjingle/talk/base/taskrunner.h new file mode 100644 index 0000000..e1c5ed6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/taskrunner.h @@ -0,0 +1,74 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_TASKRUNNER_H__ +#define TALK_BASE_TASKRUNNER_H__ + +#include <vector> + +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + +namespace talk_base { + +class Task; + +const int64 kSecToMsec = 1000; +const int64 kMsecTo100ns = 10000; +const int64 kSecTo100ns = kSecToMsec * kMsecTo100ns; + +class TaskRunner : public Task, public sigslot::has_slots<> { + public: + TaskRunner(); + virtual ~TaskRunner(); + + virtual void WakeTasks() = 0; + + // This method returns the current time in 100ns units since 1/1/1601. This + // Is the GetSystemTimeAsFileTime method on windows. + virtual int64 CurrentTime() = 0 ; + + void StartTask(Task *task); + void RunTasks(); + void PollTasks(); + + void UpdateTaskTimeout(Task *task); + + // dummy state machine - never run. + virtual int ProcessStart() { return STATE_DONE; } + + private: + std::vector<Task *> tasks_; + Task *next_timeout_task_; + bool tasks_running_; + + void RecalcNextTimeout(Task *exclude_task); +}; + +} // namespace talk_base + +#endif // TASK_BASE_TASKRUNNER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/testclient.cc b/Plugins/jingle/libjingle/talk/base/testclient.cc new file mode 100644 index 0000000..ecb45fc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/testclient.cc @@ -0,0 +1,161 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <iostream> +#include <cassert> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +#include "talk/base/testclient.h" +#include "talk/base/time.h" + + +namespace talk_base { + +// DESIGN: Each packet received is posted to ourselves as a message on the +// thread given to the constructor. When we receive the message, we +// put it into a list of packets. We take the latter step so that we +// can wait for a new packet to arrive by calling Get/Dispatch on the +// thread. + +TestClient::TestClient(AsyncPacketSocket* socket, Thread* thread) + : thread_(thread), socket_(socket) { + if (!thread_) + thread_ = Thread::Current(); + packets_ = new std::vector<Packet*>(); + socket_->SignalReadPacket.connect(this, &TestClient::OnPacket); +} + +TestClient::~TestClient() { + delete socket_; + for (unsigned i = 0; i < packets_->size(); i++) + delete (*packets_)[i]; +} + +void TestClient::Send(const char* buf, size_t size) { + int result = socket_->Send(buf, size); + if (result < 0) { + std::cerr << "send: " << std::strerror(errno) << std::endl; + exit(1); + } +} + +void TestClient::SendTo( + const char* buf, size_t size, const SocketAddress& dest) { + int result = socket_->SendTo(buf, size, dest); + if (result < 0) { + std::cerr << "sendto: " << std::strerror(errno) << std::endl; + exit(1); + } +} + +void TestClient::OnPacket( + const char* buf, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* socket) { + thread_->Post(this, 0, new Packet(remote_addr, buf, size)); +} + +void TestClient::OnMessage(Message *pmsg) { + assert(pmsg->pdata); + packets_->push_back(new Packet(*static_cast<Packet*>(pmsg->pdata))); +} + +TestClient::Packet* TestClient::NextPacket() { + + // If no packets are currently available, we go into a get/dispatch loop for + // at most 1 second. If, during the loop, a packet arrives, then we can stop + // early and return it. + // + // Note that the case where no packet arrives is important. We often want to + // test that a packet does not arrive. + + if (packets_->size() == 0) { + uint32 msNext = 1000; + uint32 msEnd = GetMillisecondCount() + msNext; + + while (true) { + Message msg; + if (!thread_->Get(&msg, msNext)) + break; + thread_->Dispatch(&msg); + + uint32 msCur = GetMillisecondCount(); + if (msCur >= msEnd) + break; + msNext = msEnd - msCur; + + if (packets_->size() > 0) + break; + } + } + + if (packets_->size() == 0) { + return 0; + } else { + // Return the first packet placed in the queue. + Packet* packet = packets_->front(); + packets_->erase(packets_->begin()); + return packet; + } +} + +void TestClient::CheckNextPacket( + const char* buf, size_t size, SocketAddress* addr) { + Packet* packet = NextPacket(); + assert(packet); + assert(packet->size == size); + assert(std::memcmp(packet->buf, buf, size) == 0); + if (addr) + *addr = packet->addr; +} + +void TestClient::CheckNoPacket() { + Packet* packet = NextPacket(); + assert(!packet); +} + +TestClient::Packet::Packet(const SocketAddress& a, const char* b, size_t s) + : addr(a), buf(0), size(s) { + buf = new char[size]; + memcpy(buf, b, size); +} + +TestClient::Packet::Packet(const Packet& p) + : addr(p.addr), buf(0), size(p.size) { + buf = new char[size]; + memcpy(buf, p.buf, size); +} + +TestClient::Packet::~Packet() { + delete buf; +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/testclient.h b/Plugins/jingle/libjingle/talk/base/testclient.h new file mode 100644 index 0000000..012e858 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/testclient.h @@ -0,0 +1,89 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_TESTCLIENT_H__ +#define TALK_BASE_TESTCLIENT_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/thread.h" +#include <vector> + +namespace talk_base { + +class TestClient : public MessageHandler, public sigslot::has_slots<> { +public: + // Creates a client that will send and receive with the given socket and + // will post itself messages with the given thread. + TestClient(AsyncPacketSocket* socket, Thread* thread = 0); + ~TestClient(); + + // Sends using the clients socket. + void Send(const char* buf, size_t size); + + // Sends using the clients socket to the given destination. + void SendTo(const char* buf, size_t size, const SocketAddress& dest); + + // Records the contents of a packet that was received. + struct Packet : public MessageData { + Packet(const SocketAddress& a, const char* b, size_t s); + Packet(const Packet& p); + virtual ~Packet(); + + SocketAddress addr; + char* buf; + size_t size; + }; + + // Returns the next packet received by the client or 0 if none is received + // within a reasonable amount of time. The caller must delete the packet + // when done with it. + Packet* NextPacket(); + + // Checks that the next packet has the given contents. Returns the remote + // address that the packet was sent from. + void CheckNextPacket(const char* buf, size_t len, SocketAddress* addr); + + // Checks that no packets have arrived or will arrive in the next second. + void CheckNoPacket(); + + // Messagehandler: + void OnMessage(Message *pmsg); + +private: + Thread* thread_; + AsyncPacketSocket* socket_; + std::vector<Packet*>* packets_; + + // Slot for packets read on the socket. + void OnPacket( + const char* buf, size_t len, const SocketAddress& remote_addr, + AsyncPacketSocket* socket); +}; + +} // namespace talk_base + +#endif // TALK_BASE_TESTCLIENT_H__ diff --git a/Plugins/jingle/libjingle/talk/base/thread.cc b/Plugins/jingle/libjingle/talk/base/thread.cc new file mode 100644 index 0000000..8fc45e1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/thread.cc @@ -0,0 +1,353 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef POSIX +extern "C" { +#include <sys/time.h> +} +#endif + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#include "talk/base/time.h" + +#define MSDEV_SET_THREAD_NAME 0x406D1388 + +namespace talk_base { + +ThreadManager g_thmgr; + +#ifdef POSIX +pthread_key_t ThreadManager::key_; + +ThreadManager::ThreadManager() { + pthread_key_create(&key_, NULL); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + pthread_key_delete(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)pthread_getspecific(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + pthread_setspecific(key_, thread); +} +#endif + +#ifdef WIN32 +DWORD ThreadManager::key_; + +ThreadManager::ThreadManager() { + key_ = TlsAlloc(); + main_thread_ = new Thread(); + SetCurrent(main_thread_); +} + +ThreadManager::~ThreadManager() { + TlsFree(key_); + delete main_thread_; +} + +Thread *ThreadManager::CurrentThread() { + return (Thread *)TlsGetValue(key_); +} + +void ThreadManager::SetCurrent(Thread *thread) { + TlsSetValue(key_, thread); +} +#endif + +void ThreadManager::Add(Thread *thread) { + CritScope cs(&crit_); + threads_.push_back(thread); +} + +void ThreadManager::Remove(Thread *thread) { + CritScope cs(&crit_); + threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end()); +} + +Thread::Thread(SocketServer* ss) : MessageQueue(ss), priority_(PRIORITY_NORMAL) { + g_thmgr.Add(this); + started_ = false; + has_sends_ = false; +} + +Thread::~Thread() { + Stop(); + if (active_) + Clear(NULL); + g_thmgr.Remove(this); +} + +#ifdef POSIX +void Thread::Start() { + pthread_attr_t attr; + pthread_attr_init(&attr); + if (priority_ == PRIORITY_IDLE) { + struct sched_param param; + pthread_attr_getschedparam(&attr, ¶m); + param.sched_priority = 15; // +15 = + pthread_attr_setschedparam(&attr, ¶m); + } + pthread_create(&thread_, &attr, PreRun, this); + started_ = true; +} + +void Thread::Join() { + if (started_) { + void *pv; + pthread_join(thread_, &pv); + } +} +#endif + +#ifdef WIN32 + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; +} THREADNAME_INFO; + +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + { + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + } + __try + { + RaiseException(MSDEV_SET_THREAD_NAME, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} + +void Thread::Start() { + DWORD flags = 0; + if (priority_ != PRIORITY_NORMAL) { + flags = CREATE_SUSPENDED; + } + thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, this, flags, NULL); + if (thread_) { + if (priority_ != PRIORITY_NORMAL) { + if (priority_ == PRIORITY_IDLE) { + ::SetThreadPriority(thread_, THREAD_PRIORITY_IDLE); + } + ::ResumeThread(thread_); + } + } + started_ = true; +} + +void Thread::Join() { + if (started_) { + WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + started_ = false; + } +} +#endif + +void *Thread::PreRun(void *pv) { + Thread *thread = (Thread *)pv; + ThreadManager::SetCurrent(thread); +#if defined(WIN32) && defined(_DEBUG) + char buf[256]; + _snprintf(buf, sizeof(buf), "Thread 0x%.8x", thread); + SetThreadName(GetCurrentThreadId(), buf); +#endif + thread->Run(); + return NULL; +} + +void Thread::Run() { + ProcessMessages(kForever); +} + +void Thread::Stop() { + MessageQueue::Stop(); + Join(); +} + +void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { + if (fStop_) + return; + + // Sent messages are sent to the MessageHandler directly, in the context + // of "thread", like Win32 SendMessage. If in the right context, + // call the handler directly. + + Message msg; + msg.phandler = phandler; + msg.message_id = id; + msg.pdata = pdata; + if (IsCurrent()) { + phandler->OnMessage(&msg); + return; + } + + AutoThread thread; + Thread *current_thread = Thread::Current(); + ASSERT(current_thread != NULL); // AutoThread ensures this + + bool ready = false; + { + CritScope cs(&crit_); + EnsureActive(); + _SendMessage smsg; + smsg.thread = current_thread; + smsg.msg = msg; + smsg.ready = &ready; + sendlist_.push_back(smsg); + has_sends_ = true; + } + + // Wait for a reply + + ss_->WakeUp(); + + bool waited = false; + while (!ready) { + current_thread->ReceiveSends(); + current_thread->socketserver()->Wait(kForever, false); + waited = true; + } + + // Our Wait loop above may have consumed some WakeUp events for this + // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can + // cause problems for some SocketServers. + // + // Concrete example: + // Win32SocketServer on thread A calls Send on thread B. While processing the + // message, thread B Posts a message to A. We consume the wakeup for that + // Post while waiting for the Send to complete, which means that when we exit + // this loop, we need to issue another WakeUp, or else the Posted message + // won't be processed in a timely manner. + + if (waited) { + current_thread->socketserver()->WakeUp(); + } +} + +void Thread::ReceiveSends() { + // Before entering critical section, check boolean. + + if (!has_sends_) + return; + + // Receive a sent message. Cleanup scenarios: + // - thread sending exits: We don't allow this, since thread can exit + // only via Join, so Send must complete. + // - thread receiving exits: Wakeup/set ready in Thread::Clear() + // - object target cleared: Wakeup/set ready in Thread::Clear() + crit_.Enter(); + while (!sendlist_.empty()) { + _SendMessage smsg = sendlist_.front(); + sendlist_.pop_front(); + crit_.Leave(); + smsg.msg.phandler->OnMessage(&smsg.msg); + crit_.Enter(); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + } + has_sends_ = false; + crit_.Leave(); +} + +void Thread::Clear(MessageHandler *phandler, uint32 id) { + CritScope cs(&crit_); + + // Remove messages on sendlist_ with phandler + // Object target cleared: remove from send list, wakeup/set ready + // if sender not NULL. + + std::list<_SendMessage>::iterator iter = sendlist_.begin(); + while (iter != sendlist_.end()) { + _SendMessage smsg = *iter; + if (phandler == NULL || smsg.msg.phandler == phandler) { + if (id == (uint32)-1 || smsg.msg.message_id == id) { + iter = sendlist_.erase(iter); + *smsg.ready = true; + smsg.thread->socketserver()->WakeUp(); + continue; + } + } + ++iter; + } + + MessageQueue::Clear(phandler, id); +} + +bool Thread::ProcessMessages(int cmsLoop) { + uint32 msEnd; + if (cmsLoop != kForever) + msEnd = GetMillisecondCount() + cmsLoop; + int cmsNext = cmsLoop; + + while (true) { + Message msg; + if (!Get(&msg, cmsNext)) + return false; + Dispatch(&msg); + + if (cmsLoop != kForever) { + uint32 msCur = GetMillisecondCount(); + if (msCur >= msEnd) + return true; + cmsNext = msEnd - msCur; + } + } +} + +AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { + if (!ThreadManager::CurrentThread()) { + ThreadManager::SetCurrent(this); + } +} + +AutoThread::~AutoThread() { + if (ThreadManager::CurrentThread() == this) { + ThreadManager::SetCurrent(NULL); + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/thread.h b/Plugins/jingle/libjingle/talk/base/thread.h new file mode 100644 index 0000000..5da0aed --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/thread.h @@ -0,0 +1,161 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_THREAD_H__ +#define TALK_BASE_THREAD_H__ + +#include <algorithm> +#include <list> +#include <vector> + +#ifdef POSIX +#include <pthread.h> +#endif + +#include "talk/base/messagequeue.h" + +#ifdef WIN32 +#include "talk/base/win32.h" +#endif + +namespace talk_base { + +class Thread; + +class ThreadManager { +public: + ThreadManager(); + ~ThreadManager(); + + static Thread *CurrentThread(); + static void SetCurrent(Thread *thread); + void Add(Thread *thread); + void Remove(Thread *thread); + +private: + Thread *main_thread_; + std::vector<Thread *> threads_; + CriticalSection crit_; + +#ifdef POSIX + static pthread_key_t key_; +#endif + +#ifdef WIN32 + static DWORD key_; +#endif +}; + +class Thread; + +struct _SendMessage { + _SendMessage() {} + Thread *thread; + Message msg; + bool *ready; +}; + +enum ThreadPriority { + PRIORITY_NORMAL, + PRIORITY_IDLE, +}; + +class Thread : public MessageQueue { +public: + Thread(SocketServer* ss = 0); + virtual ~Thread(); + + static inline Thread* Current() { + return ThreadManager::CurrentThread(); + } + inline bool IsCurrent() const { + return (ThreadManager::CurrentThread() == this); + } + + void SetPriority(ThreadPriority priority) { + priority_ = priority; + } + + virtual void Start(); + virtual void Stop(); + + // By default, Thread::Run() calls ProcessMessages(kForever). To do other + // work, override Run(). To receive and dispatch messages, call + // ProcessMessages occasionally. + virtual void Run(); + + virtual void Send(MessageHandler *phandler, uint32 id = 0, + MessageData *pdata = NULL); + + // From MessageQueue + virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1); + virtual void ReceiveSends(); + + // ProcessMessages will process I/O and dispatch messages until: + // 1) cms milliseconds have elapsed (returns true) + // 2) Stop() is called (returns false) + bool ProcessMessages(int cms); + +#ifdef WIN32 + HANDLE GetHandle() { + return thread_; + } +#endif + +private: + static void *PreRun(void *pv); + void Join(); + + std::list<_SendMessage> sendlist_; + ThreadPriority priority_; + bool started_; + bool has_sends_; + +#ifdef POSIX + pthread_t thread_; +#endif + +#ifdef WIN32 + HANDLE thread_; +#endif + + friend class ThreadManager; +}; + +// AutoThread automatically installs itself at construction +// uninstalls at destruction, if a Thread object is +// _not already_ associated with the current OS thread. + +class AutoThread : public Thread { +public: + AutoThread(SocketServer* ss = 0); + virtual ~AutoThread(); +}; + +} // namespace talk_base + +#endif // TALK_BASE_THREAD_H__ diff --git a/Plugins/jingle/libjingle/talk/base/time.cc b/Plugins/jingle/libjingle/talk/base/time.cc new file mode 100644 index 0000000..d4a61ac --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/time.cc @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <iostream> +#include <cstdlib> +#include <cstring> + +#include "talk/base/time.h" + +namespace talk_base { + +#ifdef POSIX +#include <sys/time.h> +uint32 Time() { + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} +#endif + +#ifdef WIN32 +#include <windows.h> +uint32 Time() { + return GetTickCount(); +} +#endif + +uint32 StartTime() { + // Close to program execution time + static const uint32 g_start = Time(); + return g_start; +} + +// Make sure someone calls it so that it gets initialized +static uint32 ignore = StartTime(); + +uint32 ElapsedTime() { + return TimeDiff(Time(), StartTime()); +} + +bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) { + if (earlier <= later) { + return ((earlier <= middle) && (middle <= later)); + } else { + return !((later < middle) && (middle < earlier)); + } +} + +int32 TimeDiff(uint32 later, uint32 earlier) { + uint32 LAST = 0xFFFFFFFF; + uint32 HALF = 0x80000000; + if (TimeIsBetween(earlier + HALF, later, earlier)) { + if (earlier <= later) { + return static_cast<long>(later - earlier); + } else { + return static_cast<long>(later + (LAST - earlier) + 1); + } + } else { + if (later <= earlier) { + return -static_cast<long>(earlier - later); + } else { + return -static_cast<long>(earlier + (LAST - later) + 1); + } + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/time.h b/Plugins/jingle/libjingle/talk/base/time.h new file mode 100644 index 0000000..9088aae --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/time.h @@ -0,0 +1,53 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_TIME_H__ +#define TALK_BASE_TIME_H__ + +#include "talk/base/basictypes.h" + +namespace talk_base { + +// Returns the current time in milliseconds. +uint32 Time(); + +// Approximate time when the program started. +uint32 StartTime(); + +// Elapsed milliseconds since StartTime() +uint32 ElapsedTime(); + +// TODO: Delete this old version. +#define GetMillisecondCount Time + +// Comparisons between time values, which can wrap around. +bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier); +int32 TimeDiff(uint32 later, uint32 earlier); + +} // namespace talk_base + +#endif // TALK_BASE_TIME_H__ diff --git a/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc b/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc new file mode 100644 index 0000000..2c2732f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/unixfilesystem.cc @@ -0,0 +1,223 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ +#include <errno.h> +#include <cassert> + +#include "talk/base/basicdefs.h" +#include "talk/base/pathutils.h" +#include "talk/base/fileutils.h" +#include "talk/base/stringutils.h" +#include "talk/base/stream.h" + +#include "talk/base/unixfilesystem.h" + +namespace talk_base { + +bool UnixFilesystem::CreateFolderI(const Pathname &path) { + LOG(LS_INFO) << "Creating folder: " << path.pathname(); + int len = path.pathname().length(); + const char *pathname = path.pathname().c_str(); + if ((len <= 0) || (pathname[len-1] != '/')) + return false; + struct stat st; + int res = ::stat(pathname, &st); + if (res == 0) { + // Something exists at this location, check if it is a directory + return S_ISDIR(st.st_mode) != 0; + } else if (errno != ENOENT) { + // Unexpected error + return false; + } + // Directory doesn't exist, look up one directory level + do { + --len; + } while ((len > 0) && (pathname[len-1] !='/')); + + char *newstring = new char[len+1]; + strncpy(newstring, pathname, len); + newstring[len] = '\0'; + + if (!CreateFolder(Pathname(newstring))) { + delete[] newstring; + return false; + } + delete[] newstring; + std::string no_slash(path.pathname(), 0, path.pathname().length()-1); + + return (::mkdir(no_slash.c_str(), 0755) == 0); + } + +FileStream *UnixFilesystem::OpenFileI(const Pathname &filename, + const std::string &mode) { + talk_base::FileStream *fs = new talk_base::FileStream(); + if (fs) + fs->Open(filename.pathname().c_str(), mode.c_str()); + return fs; +} + +bool UnixFilesystem::DeleteFileI(const Pathname &filename) { + LOG(LS_INFO) << "Deleting " << filename.pathname(); + + if (IsFolder(filename)) { + Pathname dir; + dir.SetFolder(filename.pathname()); + DirectoryIterator di; + di.Iterate(dir.pathname()); + while(di.Next()) { + if (di.Name() == "." || di.Name() == "..") + continue; + Pathname subdir; + subdir.SetFolder(filename.pathname()); + subdir.SetFilename(di.Name()); + + if (!DeleteFile(subdir.pathname())) + return false; + } + std::string no_slash(filename.pathname(), 0, filename.pathname().length()-1); + return ::rmdir(no_slash.c_str()) == 0; + } + return ::unlink(filename.pathname().c_str()) == 0; +} + +bool UnixFilesystem::GetTemporaryFolderI(Pathname &pathname, bool create, + const std::string *append) { + pathname.SetPathname("/tmp"); + if (append) { + pathname.AppendFolder(*append); + if (create) + CreateFolder(pathname); + } +} + +std::string UnixFilesystem::TempFilenameI(const Pathname &dir, const std::string &prefix) { + int len = dir.pathname().size() + prefix.size() + 2 + 6; + char *tempname = new char[len]; + + snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(), prefix.c_str()); + int fd = ::mkstemp(tempname); + if (fd != -1) + ::close(fd); + std::string ret(tempname); + delete[] tempname; + + return ret; +} + +bool UnixFilesystem::MoveFileI(const Pathname &old_path, const Pathname &new_path) +{ + LOG(LS_INFO) << "Moving " << old_path.pathname() << " to " << new_path.pathname(); + if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) { + if (errno != EXDEV) + return false; + if (!CopyFile(old_path, new_path)) + return false; + if (!DeleteFile(old_path)) + return false; + } + return true; +} + +bool UnixFilesystem::IsFolderI(const Pathname &path) +{ + struct stat st; + if (stat(path.pathname().c_str(), &st) < 0) + return false; + + return S_ISDIR(st.st_mode); +} + +bool UnixFilesystem::CopyFileI(const Pathname &old_path, const Pathname &new_path) +{ + LOG(LS_INFO) << "Copying " << old_path.pathname() << " to " << new_path.pathname(); + char buf[256]; + size_t len; + if (IsFolder(old_path)) { + Pathname new_dir; + new_dir.SetFolder(new_path.pathname()); + Pathname old_dir; + old_dir.SetFolder(old_path.pathname()); + + if (!CreateFolder(new_dir)) + return false; + DirectoryIterator di; + di.Iterate(old_dir.pathname()); + while(di.Next()) { + if (di.Name() == "." || di.Name() == "..") + continue; + Pathname source; + Pathname dest; + source.SetFolder(old_dir.pathname()); + dest.SetFolder(new_path.pathname()); + source.SetFilename(di.Name()); + dest.SetFilename(di.Name()); + + if (!CopyFile(source, dest)) + return false; + } + return true; + } + + StreamInterface *source = OpenFile(old_path, "rb"); + if (!source) + return false; + + StreamInterface *dest = OpenFile(new_path, "wb"); + if (!dest) { + delete source; + return false; + } + + while (source->Read(buf, sizeof(buf), &len, NULL) == talk_base::SR_SUCCESS) + dest->Write(buf, len, NULL, NULL); + + delete source; + delete dest; + return true; +} + +bool UnixFilesystem::IsTemporaryPathI(const Pathname& pathname) +{ + return (!strncmp(pathname.pathname().c_str(), "/tmp/", strlen("/tmp/"))); +} + +bool UnixFilesystem::FileExistsI(const Pathname& pathname) +{ + struct stat st; + int res = ::stat(pathname.pathname().c_str(), &st); + return res == 0; +} + +bool UnixFilesystem::GetFileSizeI(const Pathname& pathname, size_t *size) +{ + struct stat st; + if (::stat(pathname.pathname().c_str(), &st) != 0) + return false; + *size = st.st_size; + return true; +} + +} diff --git a/Plugins/jingle/libjingle/talk/base/unixfilesystem.h b/Plugins/jingle/libjingle/talk/base/unixfilesystem.h new file mode 100644 index 0000000..bd145bb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/unixfilesystem.h @@ -0,0 +1,86 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _TALK_BASE_UNIXFILESYSTEM_H__ +#define _TALK_BASE_UNIXFILESYSTEM_H__ + +#include "fileutils.h" + +namespace talk_base { + +class UnixFilesystem : public Filesystem{ + public: + + virtual bool CreateFolderI(const Pathname &pathname); + + // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise, + // returns NULL. + virtual FileStream *OpenFileI(const Pathname &filename, + const std::string &mode); + + // This will attempt to delete the path located at filename. If filename is a file, + // it will be unlinked. If the path is a directory, it will recursively unlink and remove + // all the files and directory within it + virtual bool DeleteFileI(const Pathname &filename); + + // Creates a directory. This will call itself recursively to create /foo/bar even if + // /foo does not exist. + // Returns TRUE if function succeeds + + // This moves a file from old_path to new_path, where "file" can be a plain file + // or directory, which will be moved recursively. + // Returns true if function succeeds. + virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path); + + // This copies a file from old_path to _new_path where "file" can be a plain file + // or directory, which will be copied recursively. + // Returns true if function succeeds + virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path); + + // Returns true if a pathname is a directory + virtual bool IsFolderI(const Pathname& pathname); + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPathI(const Pathname& pathname); + + // Returns true of pathname represents an existing file + virtual bool FileExistsI(const Pathname& pathname); + + virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix); + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exists) + virtual bool GetTemporaryFolderI(Pathname &path, bool create, + const std::string *append); + + virtual bool GetFileSizeI(const Pathname &path, size_t *size); + + }; + +} + +#endif // _UNIXFILESYSTEM_H__ diff --git a/Plugins/jingle/libjingle/talk/base/urlencode.cc b/Plugins/jingle/libjingle/talk/base/urlencode.cc new file mode 100644 index 0000000..b654c35 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/urlencode.cc @@ -0,0 +1,120 @@ +#include "talk/base/urlencode.h" + +static int HexPairValue(const char * code) { + int value = 0; + const char * pch = code; + for (;;) { + int digit = *pch++; + if (digit >= '0' && digit <= '9') { + value += digit - '0'; + } + else if (digit >= 'A' && digit <= 'F') { + value += digit - 'A' + 10; + } + else if (digit >= 'a' && digit <= 'f') { + value += digit - 'a' + 10; + } + else { + return -1; + } + if (pch == code + 2) + return value; + value <<= 4; + } +} + +int UrlDecode(const char *source, char *dest) +{ + char * start = dest; + + while (*source) { + switch (*source) { + case '+': + *(dest++) = ' '; + break; + case '%': + if (source[1] && source[2]) { + int value = HexPairValue(source + 1); + if (value >= 0) { + *(dest++) = value; + source += 2; + } + else { + *dest++ = '?'; + } + } + else { + *dest++ = '?'; + } + break; + default: + *dest++ = *source; + } + source++; + } + + *dest = 0; + return dest - start; +} + +int UrlEncode(const char *source, char *dest, unsigned max) +{ + static const char *digits = "0123456789ABCDEF"; + unsigned char ch; + unsigned len = 0; + char *start = dest; + + while (len < max - 4 && *source) + { + ch = (unsigned char)*source; + if (*source == ' ') { + *dest++ = '+'; + } + else if (isalnum(ch) || strchr("-_.!~*'()", ch)) { + *dest++ = *source; + } + else { + *dest++ = '%'; + *dest++ = digits[(ch >> 4) & 0x0F]; + *dest++ = digits[ ch & 0x0F]; + } + source++; + } + *dest = 0; + return start - dest; +} + +std::string +UrlDecodeString(const std::string & encoded) { + const char * sz_encoded = encoded.c_str(); + size_t needed_length = encoded.length(); + for (const char * pch = sz_encoded; *pch; pch++) { + if (*pch == '%') + needed_length += 2; + } + needed_length += 10; + char stackalloc[64]; + char * buf = needed_length > sizeof(stackalloc)/sizeof(*stackalloc) ? + (char *)malloc(needed_length) : stackalloc; + UrlDecode(encoded.c_str(), buf); + std::string result(buf); + if (buf != stackalloc) { + free(buf); + } + return result; +} + +std::string +UrlEncodeString(const std::string & decoded) { + const char * sz_decoded = decoded.c_str(); + size_t needed_length = decoded.length() * 3 + 3; + char stackalloc[64]; + char * buf = needed_length > sizeof(stackalloc)/sizeof(*stackalloc) ? + (char *)malloc(needed_length) : stackalloc; + UrlEncode(decoded.c_str(), buf, needed_length); + std::string result(buf); + if (buf != stackalloc) { + free(buf); + } + return result; +} diff --git a/Plugins/jingle/libjingle/talk/base/urlencode.h b/Plugins/jingle/libjingle/talk/base/urlencode.h new file mode 100644 index 0000000..9d2b3f6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/urlencode.h @@ -0,0 +1,12 @@ +#ifndef _URLENCODE_H_ +#define _URLENCODE_H_ + +#include <string> + +int UrlDecode(const char *source, char *dest); +int UrlEncode(const char *source, char *dest, unsigned max); +std::string UrlDecodeString(const std::string & encoded); +std::string UrlEncodeString(const std::string & decoded); + +#endif + diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc b/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc new file mode 100644 index 0000000..16456a2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/virtualsocket_unittest.cc @@ -0,0 +1,239 @@ +// Copyright 2006, Google Inc. + +#include <complex> +#include <iostream> +#include <cassert> + +#include "talk/base/thread.h" +#include "talk/base/virtualsocketserver.h" +#include "talk/base/testclient.h" +#include "talk/base/time.h" + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace talk_base; + +void test_basic(Thread* thread, VirtualSocketServer* ss) { + std::cout << "basic: "; + std::cout.flush(); + + SocketAddress addr1(ss->GetNextIP(), 5000); + AsyncUDPSocket* socket = CreateAsyncUDPSocket(ss); + socket->Bind(addr1); + + TestClient* client1 = new TestClient(socket); + TestClient* client2 = new TestClient(CreateAsyncUDPSocket(ss)); + + SocketAddress addr2; + client2->SendTo("foo", 3, addr1); + client1->CheckNextPacket("foo", 3, &addr2); + + SocketAddress addr3; + client1->SendTo("bizbaz", 6, addr2); + client2->CheckNextPacket("bizbaz", 6, &addr3); + assert(addr3 == addr1); + + for (int i = 0; i < 10; i++) { + client2 = new TestClient(CreateAsyncUDPSocket(ss)); + + SocketAddress addr4; + client2->SendTo("foo", 3, addr1); + client1->CheckNextPacket("foo", 3, &addr4); + assert((addr4.ip() == addr2.ip()) && (addr4.port() == addr2.port() + 1)); + + SocketAddress addr5; + client1->SendTo("bizbaz", 6, addr4); + client2->CheckNextPacket("bizbaz", 6, &addr5); + assert(addr5 == addr1); + + addr2 = addr4; + } + + std::cout << "PASS" << std::endl; +} + +// Sends at a constant rate but with random packet sizes. +struct Sender : public MessageHandler { + Sender(Thread* th, AsyncUDPSocket* s, uint32 rt) + : thread(th), socket(s), done(false), rate(rt), count(0) { + last_send = GetMillisecondCount(); + thread->PostDelayed(NextDelay(), this, 1); + } + + uint32 NextDelay() { + uint32 size = (rand() % 4096) + 1; + return 1000 * size / rate; + } + + void OnMessage(Message* pmsg) { + assert(pmsg->message_id == 1); + + if (done) + return; + + uint32 cur_time = GetMillisecondCount(); + uint32 delay = cur_time - last_send; + uint32 size = rate * delay / 1000; + size = std::min(size, uint32(4096)); + size = std::max(size, uint32(4)); + + count += size; + *reinterpret_cast<uint32*>(dummy) = cur_time; + socket->Send(dummy, size); + + last_send = cur_time; + thread->PostDelayed(NextDelay(), this, 1); + } + + Thread* thread; + AsyncUDPSocket* socket; + bool done; + uint32 rate; // bytes per second + uint32 count; + uint32 last_send; + char dummy[4096]; +}; + +struct Receiver : public MessageHandler, public sigslot::has_slots<> { + Receiver(Thread* th, AsyncUDPSocket* s, uint32 bw) + : thread(th), socket(s), bandwidth(bw), done(false), count(0), + sec_count(0), sum(0), sum_sq(0), samples(0) { + socket->SignalReadPacket.connect(this, &Receiver::OnReadPacket); + thread->PostDelayed(1000, this, 1); + } + + ~Receiver() { + thread->Clear(this); + } + + void OnReadPacket( + const char* data, size_t size, const SocketAddress& remote_addr, + AsyncPacketSocket* s) { + assert(s == socket); + assert(size >= 4); + + count += size; + sec_count += size; + + uint32 send_time = *reinterpret_cast<const uint32*>(data); + uint32 recv_time = GetMillisecondCount(); + uint32 delay = recv_time - send_time; + sum += delay; + sum_sq += delay * delay; + samples += 1; + } + + void OnMessage(Message* pmsg) { + assert(pmsg->message_id == 1); + // It is always possible for us to receive more than expected because + // packets can be further delayed in delivery. + if (bandwidth > 0) + assert(sec_count <= 5 * bandwidth / 4); + sec_count = 0; + thread->PostDelayed(1000, this, 1); + } + + Thread* thread; + AsyncUDPSocket* socket; + uint32 bandwidth; + bool done; + uint32 count; + uint32 sec_count; + double sum; + double sum_sq; + uint32 samples; +}; + +void test_bandwidth(Thread* thread, VirtualSocketServer* ss) { + std::cout << "bandwidth: "; + std::cout.flush(); + + AsyncUDPSocket* send_socket = CreateAsyncUDPSocket(ss); + AsyncUDPSocket* recv_socket = CreateAsyncUDPSocket(ss); + assert(send_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0); + assert(recv_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0); + assert(send_socket->Connect(recv_socket->GetLocalAddress()) >= 0); + + uint32 bandwidth = 64 * 1024; + ss->set_bandwidth(bandwidth); + + Sender sender(thread, send_socket, 80 * 1024); + Receiver receiver(thread, recv_socket, bandwidth); + + Thread* pthMain = Thread::Current(); + pthMain->ProcessMessages(5000); + sender.done = true; + pthMain->ProcessMessages(5000); + + assert(receiver.count >= 5 * 3 * bandwidth / 4); + assert(receiver.count <= 6 * bandwidth); // queue could drain for 1 sec + + delete send_socket; + delete recv_socket; + + ss->set_bandwidth(0); + + std::cout << "PASS" << std::endl; +} + +void test_delay(Thread* thread, VirtualSocketServer* ss) { + std::cout << "delay: "; + std::cout.flush(); + + uint32 mean = 2000; + uint32 stddev = 500; + + ss->set_delay_mean(mean); + ss->set_delay_stddev(stddev); + ss->UpdateDelayDistribution(); + + AsyncUDPSocket* send_socket = CreateAsyncUDPSocket(ss); + AsyncUDPSocket* recv_socket = CreateAsyncUDPSocket(ss); + assert(send_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0); + assert(recv_socket->Bind(SocketAddress(ss->GetNextIP(), 1000)) >= 0); + assert(send_socket->Connect(recv_socket->GetLocalAddress()) >= 0); + + Sender sender(thread, send_socket, 64 * 1024); + Receiver receiver(thread, recv_socket, 0); + + Thread* pthMain = Thread::Current(); + pthMain->ProcessMessages(5000); + sender.done = true; + pthMain->ProcessMessages(5000); + + double sample_mean = receiver.sum / receiver.samples; + double num = receiver.sum_sq - 2 * sample_mean * receiver.sum + + receiver.samples * sample_mean * sample_mean; + double sample_stddev = std::sqrt(num / (receiver.samples - 1)); +std::cout << "mean=" << sample_mean << " dev=" << sample_stddev << std::endl; + + assert(0.9 * mean <= sample_mean); + assert(sample_mean <= 1.1 * mean); + assert(0.9 * stddev <= sample_stddev); + assert(sample_stddev <= 1.1 * stddev); + + delete send_socket; + delete recv_socket; + + ss->set_delay_mean(0); + ss->set_delay_stddev(0); + ss->UpdateDelayDistribution(); + + std::cout << "PASS" << std::endl; +} + +int main(int argc, char* argv) { + Thread *pthMain = Thread::Current(); + VirtualSocketServer* ss = new VirtualSocketServer(); + pthMain->set_socketserver(ss); + + test_basic(pthMain, ss); + test_bandwidth(pthMain, ss); + test_delay(pthMain, ss); + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc new file mode 100644 index 0000000..884b324 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.cc @@ -0,0 +1,616 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <cstring> +#include <iostream> +#include <vector> +#include <errno.h> + +#include "talk/base/virtualsocketserver.h" +#include "talk/base/common.h" +#include "talk/base/time.h" + +namespace talk_base { + +const uint32 HEADER_SIZE = 28; // IP + UDP headers + +const uint32 MSG_ID_PACKET = 1; +// TODO: Add a message type for new connections. + +// Packets are passed between sockets as messages. We copy the data just like +// the kernel does. +class Packet : public MessageData { +public: + Packet(const char* data, size_t size, const SocketAddress& from) + : size_(size), from_(from) { + assert(data); + assert(size_ >= 0); + data_ = new char[size_]; + std::memcpy(data_, data, size_); + } + + virtual ~Packet() { + delete data_; + } + + const char* data() const { return data_; } + size_t size() const { return size_; } + const SocketAddress& from() const { return from_; } + + // Remove the first size bytes from the data. + void Consume(size_t size) { + assert(size < size_); + size_ -= size; + char* new_data = new char[size_]; + std::memcpy(new_data, data_, size); + delete[] data_; + data_ = new_data; + } + +private: + char* data_; + size_t size_; + SocketAddress from_; +}; + +// Implements the socket interface using the virtual network. Packets are +// passed as messages using the message queue of the socket server. +class VirtualSocket : public AsyncSocket, public MessageHandler { +public: + VirtualSocket( + VirtualSocketServer* server, int type, bool async, uint32 ip) + : server_(server), type_(type), async_(async), connected_(false), + local_ip_(ip), readable_(true), queue_size_(0) { + assert((type_ == SOCK_DGRAM) || (type_ == SOCK_STREAM)); + packets_ = new std::vector<Packet*>(); + } + + ~VirtualSocket() { + Close(); + + for (unsigned i = 0; i < packets_->size(); i++) + delete (*packets_)[i]; + delete packets_; + } + + SocketAddress GetLocalAddress() const { + return local_addr_; + } + + SocketAddress GetRemoteAddress() const { + return remote_addr_; + } + + int Bind(const SocketAddress& addr) { + assert(addr.port() != 0); + int result = server_->Bind(addr, this); + if (result >= 0) + local_addr_ = addr; + else + error_ = EADDRINUSE; + return result; + } + + int Connect(const SocketAddress& addr) { + assert(!connected_); + connected_ = true; + remote_addr_ = addr; + assert(type_ == SOCK_DGRAM); // stream not yet implemented + return 0; + } + + int Close() { + if (!local_addr_.IsAny()) + server_->Unbind(local_addr_, this); + + connected_ = false; + local_addr_ = SocketAddress(); + remote_addr_ = SocketAddress(); + return 0; + } + + int Send(const void *pv, size_t cb) { + assert(connected_); + return SendInternal(pv, cb, remote_addr_); + } + + int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { + assert(!connected_); + return SendInternal(pv, cb, addr); + } + + int SendInternal(const void *pv, size_t cb, const SocketAddress& addr) { + // If we have not been assigned a local port, then get one. + if (local_addr_.IsAny()) { + local_addr_.SetIP(local_ip_); + int result = server_->Bind(this, &local_addr_); + if (result < 0) { + local_addr_.SetIP(0); + error_ = EADDRINUSE; + return result; + } + } + + // Send the data in a message to the appropriate socket. + return server_->Send(this, pv, cb, local_addr_, addr); + } + + int Recv(void *pv, size_t cb) { + SocketAddress addr; + return RecvFrom(pv, cb, &addr); + } + + int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { + // If we don't have a packet, then either error or wait for one to arrive. + if (packets_->size() == 0) { + if (async_) { + error_ = EAGAIN; + return -1; + } + while (packets_->size() == 0) { + Message msg; + server_->msg_queue_->Get(&msg); + server_->msg_queue_->Dispatch(&msg); + } + } + + // Return the packet at the front of the queue. + Packet* packet = packets_->front(); + *paddr = packet->from(); + int size = (int)packet->size(); + if (size <= (int)cb) { + std::memcpy(pv, packet->data(), size); + packets_->erase(packets_->begin()); + delete packet; + return size; + } else { + std::memcpy(pv, packet->data(), cb); + packet->Consume(cb); + return (int)cb; + } + } + + int Listen(int backlog) { + assert(false); // not yet implemented + return 0; + } + + Socket* Accept(SocketAddress *paddr) { + assert(false); // not yet implemented + return 0; + } + + bool readable() { return readable_; } + void set_readable(bool value) { readable_ = value; } + + bool writable() { return false; } + void set_writable(bool value) { + // TODO: Send ourselves messages (delayed after the first) to give them a + // chance to write. + assert(false); + } + + int GetError() const { + return error_; + } + + void SetError(int error) { + error_ = error; + } + + ConnState GetState() const { + return connected_ ? CS_CONNECTED : CS_CLOSED; + } + + int SetOption(Option opt, int value) { + return 0; + } + + int EstimateMTU(uint16* mtu) { + if (!connected_) + return ENOTCONN; + else + return 65536; + } + + void OnMessage(Message *pmsg) { + if (pmsg->message_id == MSG_ID_PACKET) { + assert(pmsg->pdata); + Packet* packet = static_cast<Packet*>(pmsg->pdata); + + if (!readable_) + return; + + packets_->push_back(packet); + + if (async_) { + SignalReadEvent(this); + + // TODO: If the listeners don't want to read this packet now, we will + // need to send ourselves delayed messages to try again. + assert(packets_->size() == 0); + } + } else { + assert(false); + } + } + +private: + struct QueueEntry { + uint32 size; + uint32 done_time; + }; + + typedef std::deque<QueueEntry> SendQueue; + + VirtualSocketServer* server_; + int type_; + bool async_; + bool connected_; + uint32 local_ip_; + bool readable_; + SocketAddress local_addr_; + SocketAddress remote_addr_; + std::vector<Packet*>* packets_; + int error_; + SendQueue queue_; + uint32 queue_size_; + CriticalSection queue_crit_; + + friend class VirtualSocketServer; +}; + +VirtualSocketServer::VirtualSocketServer() + : fWait_(false), wait_version_(0), next_ip_(1), next_port_(45000), + bandwidth_(0), queue_capacity_(64 * 1024), delay_mean_(0), + delay_stddev_(0), delay_dist_(0), drop_prob_(0.0) { + msg_queue_ = new MessageQueue(); // uses physical socket server for Wait + bindings_ = new AddressMap(); + + UpdateDelayDistribution(); +} + +VirtualSocketServer::~VirtualSocketServer() { + delete bindings_; + delete msg_queue_; + delete delay_dist_; +} + +uint32 VirtualSocketServer::GetNextIP() { + return next_ip_++; +} + +Socket* VirtualSocketServer::CreateSocket(int type) { + return CreateSocketInternal(type); +} + +AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int type) { + return CreateSocketInternal(type); +} + +VirtualSocket* VirtualSocketServer::CreateSocketInternal(int type) { + uint32 ip = (next_ip_ > 1) ? next_ip_ - 1 : 1; + return new VirtualSocket(this, type, true, ip); +} + +bool VirtualSocketServer::Wait(int cmsWait, bool process_io) { + ASSERT(process_io); // This can't be easily supported. + + uint32 msEnd; + if (cmsWait != kForever) + msEnd = GetMillisecondCount() + cmsWait; + uint32 cmsNext = cmsWait; + + fWait_ = true; + wait_version_ += 1; + + while (fWait_) { + Message msg; + if (!msg_queue_->Get(&msg, cmsNext)) + return true; + msg_queue_->Dispatch(&msg); + + if (cmsWait != kForever) { + uint32 msCur = GetMillisecondCount(); + if (msCur >= msEnd) + return true; + cmsNext = msEnd - msCur; + } + } + return true; +} + +const uint32 MSG_WAKE_UP = 1; + +struct WakeUpMessage : public MessageData { + WakeUpMessage(uint32 ver) : wait_version(ver) {} + virtual ~WakeUpMessage() {} + + uint32 wait_version; +}; + +void VirtualSocketServer::WakeUp() { + msg_queue_->Post(this, MSG_WAKE_UP, new WakeUpMessage(wait_version_)); +} + +void VirtualSocketServer::OnMessage(Message* pmsg) { + assert(pmsg->message_id == MSG_WAKE_UP); + assert(pmsg->pdata); + WakeUpMessage* wmsg = static_cast<WakeUpMessage*>(pmsg->pdata); + if (wmsg->wait_version == wait_version_) + fWait_ = false; + delete pmsg->pdata; +} + +int VirtualSocketServer::Bind( + const SocketAddress& addr, VirtualSocket* socket) { + assert(addr.ip() > 0); // don't support any-address right now + assert(addr.port() > 0); + assert(socket); + + if (bindings_->find(addr) == bindings_->end()) { + (*bindings_)[addr] = socket; + return 0; + } else { + return -1; + } +} + +int VirtualSocketServer::Bind(VirtualSocket* socket, SocketAddress* addr) { + assert(addr->ip() > 0); // don't support any-address right now + assert(socket); + + for (int i = 0; i < 65536; i++) { + addr->SetPort(next_port_++); + if (addr->port() > 0) { + AddressMap::iterator iter = bindings_->find(*addr); + if (iter == bindings_->end()) { + (*bindings_)[*addr] = socket; + return 0; + } + } + } + + errno = EADDRINUSE; // TODO: is there a better error number? + return -1; +} + +int VirtualSocketServer::Unbind( + const SocketAddress& addr, VirtualSocket* socket) { + assert((*bindings_)[addr] == socket); + bindings_->erase(bindings_->find(addr)); + return 0; +} + +static double Random() { + return double(rand()) / RAND_MAX; +} + +int VirtualSocketServer::Send( + VirtualSocket* socket, const void *pv, size_t cb, + const SocketAddress& local_addr, const SocketAddress& remote_addr) { + + // See if we want to drop this packet. + if (Random() < drop_prob_) { + std::cerr << "Dropping packet: bad luck" << std::endl; + return 0; + } + + uint32 cur_time = GetMillisecondCount(); + uint32 send_delay; + + // Determine whether we have enough bandwidth to accept this packet. To do + // this, we need to update the send queue. Once we know it's current size, + // we know whether we can fit this packet. + // + // NOTE: There are better algorithms for maintaining such a queue (such as + // "Derivative Random Drop"); however, this algorithm is a more accurate + // simulation of what a normal network would do. + { + CritScope cs(&socket->queue_crit_); + + while ((socket->queue_.size() > 0) && + (socket->queue_.front().done_time <= cur_time)) { + assert(socket->queue_size_ >= socket->queue_.front().size); + socket->queue_size_ -= socket->queue_.front().size; + socket->queue_.pop_front(); + } + + VirtualSocket::QueueEntry entry; + entry.size = uint32(cb) + HEADER_SIZE; + + if (socket->queue_size_ + entry.size > queue_capacity_) { + std::cerr << "Dropping packet: queue capacity exceeded" << std::endl; + return 0; // not an error + } + + socket->queue_size_ += entry.size; + send_delay = SendDelay(socket->queue_size_); + entry.done_time = cur_time + send_delay; + socket->queue_.push_back(entry); + } + + // Find the delay for crossing the many virtual hops of the network. + uint32 transit_delay = GetRandomTransitDelay(); + + // Post the packet as a message to be delivered (on our own thread) + + AddressMap::iterator iter = bindings_->find(remote_addr); + if (iter != bindings_->end()) { + Packet* p = new Packet(static_cast<const char*>(pv), cb, local_addr); + uint32 delay = send_delay + transit_delay; + msg_queue_->PostDelayed(delay, iter->second, MSG_ID_PACKET, p); + } else { + std::cerr << "No one listening at " << remote_addr.ToString() << std::endl; + } + return (int)cb; +} + +uint32 VirtualSocketServer::SendDelay(uint32 size) { + if (bandwidth_ == 0) + return 0; + else + return 1000 * size / bandwidth_; +} + +void PrintFunction(std::vector<std::pair<double,double> >* f) { + for (uint32 i = 0; i < f->size(); i++) + std::cout << (*f)[i].first << '\t' << (*f)[i].second << std::endl; +} + +void VirtualSocketServer::UpdateDelayDistribution() { + Function* dist = GetDelayDistribution(); + dist = Resample(Invert(Accumulate(dist)), 0, 1); + + // We take a lock just to make sure we don't leak memory. + { + CritScope cs(&delay_crit_); + delete delay_dist_; + delay_dist_ = dist; + } +} + +const int NUM_SAMPLES = 100; // 1000; + +static double PI = 4 * std::atan(1.0); + +static double Normal(double x, double mean, double stddev) { + double a = (x - mean) * (x - mean) / (2 * stddev * stddev); + return std::exp(-a) / (stddev * sqrt(2 * PI)); +} + +#if 0 // static unused gives a warning +static double Pareto(double x, double min, double k) { + if (x < min) + return 0; + else + return k * std::pow(min, k) / std::pow(x, k+1); +} +#endif + +VirtualSocketServer::Function* VirtualSocketServer::GetDelayDistribution() { + Function* f = new Function(); + + if (delay_stddev_ == 0) { + + f->push_back(Point(delay_mean_, 1.0)); + + } else { + + double start = 0; + if (delay_mean_ >= 4 * double(delay_stddev_)) + start = delay_mean_ - 4 * double(delay_stddev_); + double end = delay_mean_ + 4 * double(delay_stddev_); + + double delay_min = 0; + if (delay_mean_ >= 1.0 * delay_stddev_) + delay_min = delay_mean_ - 1.0 * delay_stddev_; + + for (int i = 0; i < NUM_SAMPLES; i++) { + double x = start + (end - start) * i / (NUM_SAMPLES - 1); + double y = Normal(x, delay_mean_, delay_stddev_); + f->push_back(Point(x, y)); + } + + } + + return f; +} + +uint32 VirtualSocketServer::GetRandomTransitDelay() { + double delay = (*delay_dist_)[rand() % delay_dist_->size()].second; + return uint32(delay); +} + +struct FunctionDomainCmp { + bool operator ()(const VirtualSocketServer::Point& p1, const VirtualSocketServer::Point& p2) { + return p1.first < p2.first; + } + bool operator ()(double v1, const VirtualSocketServer::Point& p2) { + return v1 < p2.first; + } + bool operator ()(const VirtualSocketServer::Point& p1, double v2) { + return p1.first < v2; + } +}; + +VirtualSocketServer::Function* VirtualSocketServer::Accumulate(Function* f) { + assert(f->size() >= 1); + double v = 0; + for (Function::size_type i = 0; i < f->size() - 1; ++i) { + double dx = (*f)[i].second * ((*f)[i+1].first - (*f)[i].first); + v = (*f)[i].second = v + dx; + } + (*f)[f->size()-1].second = v; + return f; +} + +VirtualSocketServer::Function* VirtualSocketServer::Invert(Function* f) { + for (Function::size_type i = 0; i < f->size(); ++i) + std::swap((*f)[i].first, (*f)[i].second); + + std::sort(f->begin(), f->end(), FunctionDomainCmp()); + return f; +} + +VirtualSocketServer::Function* VirtualSocketServer::Resample( + Function* f, double x1, double x2) { + Function* g = new Function(); + + for (int i = 0; i < NUM_SAMPLES; i++) { + double x = x1 + (x2 - x1) * i / (NUM_SAMPLES - 1); + double y = Evaluate(f, x); + g->push_back(Point(x, y)); + } + + delete f; + return g; +} + +double VirtualSocketServer::Evaluate(Function* f, double x) { + Function::iterator iter = + std::lower_bound(f->begin(), f->end(), x, FunctionDomainCmp()); + if (iter == f->begin()) { + return (*f)[0].second; + } else if (iter == f->end()) { + assert(f->size() >= 1); + return (*f)[f->size() - 1].second; + } else if (iter->first == x) { + return iter->second; + } else { + double x1 = (iter - 1)->first; + double y1 = (iter - 1)->second; + double x2 = iter->first; + double y2 = iter->second; + return y1 + (y2 - y1) * (x - x1) / (x2 - x1); + } +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h new file mode 100644 index 0000000..d2e894a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/virtualsocketserver.h @@ -0,0 +1,156 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_VIRTUALSOCKETSERVER_H__ +#define TALK_BASE_VIRTUALSOCKETSERVER_H__ + +#include <cassert> +#include <deque> +#include <map> + +#include "talk/base/messagequeue.h" +#include "talk/base/socketserver.h" + +namespace talk_base { + +class VirtualSocket; + +// Simulates a network in the same manner as a loopback interface. The +// interface can create as many addresses as you want. All of the sockets +// created by this network will be able to communicate with one another. +class VirtualSocketServer : public SocketServer, public MessageHandler { +public: + VirtualSocketServer(); + virtual ~VirtualSocketServer(); + + // Returns a new IP not used before in this network. + uint32 GetNextIP(); + + // Limits the network bandwidth (maximum bytes per second). Zero means that + // all sends occur instantly. + uint32 bandwidth() { return bandwidth_; } + void set_bandwidth(uint32 bandwidth) { bandwidth_ = bandwidth; } + + // Limits the total size of packets that will be kept in the send queue, + // waiting for their turn to be written to the network Defaults to 64 KB. + uint32 queue_capacity() { return queue_capacity_; } + void set_queue_capacity(uint32 queue_capacity) { + queue_capacity_ = queue_capacity; + } + + // Controls the (transit) delay for packets sent in the network. This does + // not inclue the time required to sit in the send queue. Both of these + // values are measured in milliseconds. + uint32 delay_mean() { return delay_mean_; } + uint32 delay_stddev() { return delay_stddev_; } + void set_delay_mean(uint32 delay_mean) { delay_mean_ = delay_mean; } + void set_delay_stddev(uint32 delay_stddev) { + delay_stddev_ = delay_stddev; + } + + // If the (transit) delay parameters are modified, this method should be + // called to recompute the new distribution. + void UpdateDelayDistribution(); + + // Controls the (uniform) probability that any sent packet is dropped. This + // is separate from calculations to drop based on queue size. + double drop_probability() { return drop_prob_; } + void set_drop_probability(double drop_prob) { + assert((0 <= drop_prob) && (drop_prob <= 1)); + drop_prob_ = drop_prob; + } + + // SocketFactory: + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + + // SocketServer: + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + // Used to send internal wake-up messages. + virtual void OnMessage(Message* msg); + + typedef std::pair<double,double> Point; + typedef std::vector<Point> Function; + +private: + friend class VirtualSocket; + + typedef std::map<SocketAddress, VirtualSocket*> AddressMap; + + MessageQueue* msg_queue_; + bool fWait_; + uint32 wait_version_; + uint32 next_ip_; + uint16 next_port_; + AddressMap* bindings_; + + uint32 bandwidth_; + uint32 queue_capacity_; + uint32 delay_mean_; + uint32 delay_stddev_; + Function* delay_dist_; + CriticalSection delay_crit_; + + double drop_prob_; + + VirtualSocket* CreateSocketInternal(int type); + + // Attempts to bind the given socket to the given (non-zero) address. + int Bind(const SocketAddress& addr, VirtualSocket* socket); + + // Binds the given socket to the given (non-zero) IP on an unused port. + int Bind(VirtualSocket* socket, SocketAddress* addr); + + // Removes the binding for the given socket. + int Unbind(const SocketAddress& addr, VirtualSocket* socket); + + // Sends the given packet to the socket at the given address (if one exists). + int Send(VirtualSocket* socket, const void *pv, size_t cb, + const SocketAddress& local_addr, const SocketAddress& remote_addr); + + // Computes the number of milliseconds required to send a packet of this size. + uint32 SendDelay(uint32 size); + + // Returns the probability density function for the transit delay. + Function* GetDelayDistribution(); + + // Returns a random transit delay chosen from the appropriate distribution. + uint32 GetRandomTransitDelay(); + + // Basic operations on functions. Those that return a function also take + // ownership of the function given (and hence, may modify or delete it). + Function* Accumulate(Function* f); + Function* Invert(Function* f); + Function* Resample(Function* f, double x1, double x2); + double Evaluate(Function* f, double x); +}; + +} // namespace talk_base + +#endif // TALK_BASE_VIRTUALSOCKETSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/win32.h b/Plugins/jingle/libjingle/talk/base/win32.h new file mode 100644 index 0000000..082e84f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32.h @@ -0,0 +1,70 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_WIN32_H__ +#define TALK_BASE_WIN32_H__ + +#include <winsock2.h> +#include <windows.h> +#include <malloc.h> + +#include <string> + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// + +inline std::wstring ToUtf16(const std::string& str) { + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), + NULL, 0); + wchar_t *ws = static_cast<wchar_t*>(_alloca(len16 * sizeof(wchar_t))); + ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), ws, len16); + std::wstring result(ws, len16); + return result; +} + +inline std::string ToUtf8(const std::wstring& wstr) { + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(), + NULL, 0, NULL, NULL); + char* ns = static_cast<char*>(_alloca(len8)); + ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(), + ns, len8, NULL, NULL); + std::string result(ns, len8); + return result; +} + +// Convert FILETIME to time_t +void FileTimeToUnixTime(const FILETIME& ft, time_t* ut); + +// Convert time_t to FILETIME +void UnixTimeToFileTime(const time_t& ut, FILETIME * ft); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_WIN32_H__ diff --git a/Plugins/jingle/libjingle/talk/base/win32filesystem.cc b/Plugins/jingle/libjingle/talk/base/win32filesystem.cc new file mode 100644 index 0000000..0015384 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32filesystem.cc @@ -0,0 +1,194 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ +#include <errno.h> +#include <cassert> + +#include "talk/base/basicdefs.h" +#include "talk/base/convert.h" +#include "talk/base/pathutils.h" +#include "talk/base/fileutils.h" +#include "talk/base/stringutils.h" +#include "talk/base/stream.h" + +#include "talk/base/win32filesystem.h" + +namespace talk_base { + +bool Win32Filesystem::CreateFolderI(const Pathname &pathname) { + int len = pathname.pathname().length(); + + if ((len <= 0) || (pathname.pathname().c_str()[len-1] != '\\')) { + return false; + } + + DWORD res = ::GetFileAttributes(Utf16(pathname.pathname()).AsWz()); + if (res != INVALID_FILE_ATTRIBUTES) { + // Something exists at this location, check if it is a directory + return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0); + } else if ((GetLastError() != ERROR_FILE_NOT_FOUND) + && (GetLastError() != ERROR_PATH_NOT_FOUND)) { + // Unexpected error + return false; + } + // Directory doesn't exist, look up one directory level + do { + --len; + } while ((len > 0) && (pathname.pathname().c_str()[len-1] != '\\')); + + if (!CreateFolder(std::string(pathname.pathname().c_str(),len))) + return false; + + if (pathname.pathname().c_str()[0] != '\\') { + std::string long_path = std::string("\\\\?\\") + pathname.pathname(); + return (::CreateDirectory(Utf16(long_path).AsWz(), NULL) != 0); + } else { + return (::CreateDirectory(Utf16(pathname.pathname()).AsWz(), NULL) != 0); + } +} + +FileStream *Win32Filesystem::OpenFileI(const Pathname &filename, + const std::string &mode) { + talk_base::FileStream *fs = new talk_base::FileStream(); + if (fs) + fs->Open(filename.pathname().c_str(), mode.c_str()); + return fs; +} + +bool Win32Filesystem::DeleteFileI(const Pathname &filename) { + LOG(LS_INFO) << "Deleting " << filename.pathname(); + + if (IsFolder(filename)) { + Pathname dir; + dir.SetFolder(filename.pathname()); + DirectoryIterator di; + di.Iterate(dir.pathname()); + while(di.Next()) { + if (di.Name() == "." || di.Name() == "..") + continue; + Pathname subdir; + subdir.SetFolder(filename.pathname()); + subdir.SetFilename(di.Name()); + + if (!DeleteFile(subdir.pathname())) + return false; + } + std::string no_slash(filename.pathname(), 0, filename.pathname().length()-1); + return ::RemoveDirectory(Utf16(no_slash).AsWz()) == 0; + } + return ::DeleteFile(Utf16(filename.pathname()).AsWz()) == 0; +} + +bool Win32Filesystem::GetTemporaryFolderI(Pathname &pathname, bool create, + const std::string *append) { +// ASSERT(!g_application_name_.empty()); + wchar_t buffer[MAX_PATH + 1]; + if (!::GetTempPath(ARRAY_SIZE(buffer), buffer)) + return false; + if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + size_t len = strlen(buffer); + if ((len > 0) && (buffer[len-1] != '\\')) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + L"\\"); + } + if ((len > 0) && (buffer[len-1] != '\\')) { + len += talk_base::strcpyn(buffer + len, ARRAY_SIZE(buffer) - len, + L"\\"); + } + if (len >= ARRAY_SIZE(buffer) - 1) + return false; + pathname.clear(); + pathname.SetFolder(Utf8(buffer).AsSz()); + if (append != NULL) + pathname.AppendFolder(*append); + if (create) + CreateFolderI(pathname); + return true; +} + +std::string Win32Filesystem::TempFilenameI(const Pathname &dir, const std::string &prefix) { + wchar_t filename[MAX_PATH]; + if (::GetTempFileName(Utf16(dir.pathname()).AsWz(), Utf16(prefix).AsWz(), 0, filename) == 0) + return Utf8(filename).AsString(); + return ""; +} + +bool Win32Filesystem::MoveFileI(const Pathname &old_path, const Pathname &new_path) +{ + LOG(LS_INFO) << "Moving " << old_path.pathname() << " to " << new_path.pathname(); + if (_wrename(Utf16(old_path.pathname()).AsWz(), Utf16(new_path.pathname()).AsWz()) != 0) { + if (errno != EXDEV) { + printf("errno: %d\n", errno); + return false; + } + if (!CopyFile(old_path, new_path)) + return false; + if (!DeleteFile(old_path)) + return false; + } + return true; +} + +bool Win32Filesystem::IsFolderI(const Pathname &path) +{ + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (0 == ::GetFileAttributesEx(Utf16(path.pathname()).AsWz(), GetFileExInfoStandard, &data)) + return false; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; +} + +bool Win32Filesystem::FileExistsI(const Pathname &path) +{ + DWORD res = ::GetFileAttributes(Utf16(path.pathname()).AsWz()); + return res != INVALID_FILE_ATTRIBUTES; +} + +bool Win32Filesystem::CopyFileI(const Pathname &old_path, const Pathname &new_path) +{ + return ::CopyFile(Utf16(old_path.pathname()).AsWz(), Utf16(new_path.pathname()).AsWz(), TRUE) == 0; +} + +bool Win32Filesystem::IsTemporaryPathI(const Pathname& pathname) +{ + TCHAR buffer[MAX_PATH + 1]; + if (!::GetTempPath(ARRAY_SIZE(buffer), buffer)) + return false; + if (!::GetLongPathName(buffer, buffer, ARRAY_SIZE(buffer))) + return false; + return (::strnicmp(Utf16(pathname.pathname()).AsWz(), buffer, strlen(buffer)) == 0); +} + +bool Win32Filesystem::GetFileSizeI(const Pathname &pathname, size_t *size) +{ + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (::GetFileAttributesEx(Utf16(pathname.pathname()).AsWz(), GetFileExInfoStandard, &data) == 0) + return false; + *size = data.nFileSizeLow; + return true; +} + +} diff --git a/Plugins/jingle/libjingle/talk/base/win32filesystem.h b/Plugins/jingle/libjingle/talk/base/win32filesystem.h new file mode 100644 index 0000000..1c13157 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32filesystem.h @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _TALK_BASE_WIN32FILESYSTEM_H__ +#define _TALK_BASE_WIN32FILESYSTEM_H__ + +#include "fileutils.h" + +namespace talk_base { + +class Win32Filesystem : public Filesystem{ + public: + + virtual bool CreateFolderI(const Pathname &pathname); + + // Opens a file. Returns an open StreamInterface if function succeeds. Otherwise, + // returns NULL. + virtual FileStream *OpenFileI(const Pathname &filename, + const std::string &mode); + + // This will attempt to delete the path located at filename. If filename is a file, + // it will be unlinked. If the path is a directory, it will recursively unlink and remove + // all the files and directory within it + virtual bool DeleteFileI(const Pathname &filename); + + // Creates a directory. This will call itself recursively to create /foo/bar even if + // /foo does not exist. + // Returns TRUE if function succeeds + + // This moves a file from old_path to new_path, where "file" can be a plain file + // or directory, which will be moved recursively. + // Returns true if function succeeds. + virtual bool MoveFileI(const Pathname &old_path, const Pathname &new_path); + + // This copies a file from old_path to _new_path where "file" can be a plain file + // or directory, which will be copied recursively. + // Returns true if function succeeds + virtual bool CopyFileI(const Pathname &old_path, const Pathname &new_path); + + // Returns true if a pathname is a directory + virtual bool IsFolderI(const Pathname& pathname); + + // Returns true if a file exists at path + virtual bool FileExistsI(const Pathname &path); + + // Returns true if pathname represents a temporary location on the system. + virtual bool IsTemporaryPathI(const Pathname& pathname); + + + // All of the following functions set pathname and return true if successful. + // Returned paths always include a trailing backslash. + // If create is true, the path will be recursively created. + // If append is non-NULL, it will be appended (and possibly created). + + virtual std::string TempFilenameI(const Pathname &dir, const std::string &prefix); + + virtual bool GetFileSizeI(const Pathname &pathname, size_t *size); + + // A folder appropriate for storing temporary files (Contents are + // automatically deleted when the program exists) + virtual bool GetTemporaryFolderI(Pathname &path, bool create, + const std::string *append); + }; + +} + +#endif // _WIN32FILESYSTEM_H__ diff --git a/Plugins/jingle/libjingle/talk/base/win32socketserver.cc b/Plugins/jingle/libjingle/talk/base/win32socketserver.cc new file mode 100644 index 0000000..49c8fee --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32socketserver.cc @@ -0,0 +1,767 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/winping.h" +#include "talk/base/win32socketserver.h" +#include "talk/base/win32window.h" +#include <ws2tcpip.h> + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Socket +/////////////////////////////////////////////////////////////////////////////// + +static const int kfRead = 0x0001; +static const int kfWrite = 0x0002; + +// Standard MTUs +static const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + 68, // Official minimum + 0, // End of list marker +}; + +static const uint32 IP_HEADER_SIZE = 20; +static const uint32 ICMP_HEADER_SIZE = 8; + +#ifdef DEBUG +LPCSTR WSAErrorToString(int error, LPCSTR *description_result) { + LPCSTR string = "Unspecified"; + LPCSTR description = "Unspecified description"; + switch (error) { + case ERROR_SUCCESS: + string = "SUCCESS"; + description = "Operation succeeded"; + break; + case WSAEWOULDBLOCK: + string = "WSAEWOULDBLOCK"; + description = "Using a non-blocking socket, will notify later"; + break; + case WSAEACCES: + string = "WSAEACCES"; + description = "Access denied, or sharing violation"; + break; + case WSAEADDRNOTAVAIL: + string = "WSAEADDRNOTAVAIL"; + description = "Address is not valid in this context"; + break; + case WSAENETDOWN: + string = "WSAENETDOWN"; + description = "Network is down"; + break; + case WSAENETUNREACH: + string = "WSAENETUNREACH"; + description = "Network is up, but unreachable"; + break; + case WSAENETRESET: + string = "WSANETRESET"; + description = "Connection has been reset due to keep-alive activity"; + break; + case WSAECONNABORTED: + string = "WSAECONNABORTED"; + description = "Aborted by host"; + break; + case WSAECONNRESET: + string = "WSAECONNRESET"; + description = "Connection reset by host"; + break; + case WSAETIMEDOUT: + string = "WSAETIMEDOUT"; + description = "Timed out, host failed to respond"; + break; + case WSAECONNREFUSED: + string = "WSAECONNREFUSED"; + description = "Host actively refused connection"; + break; + case WSAEHOSTDOWN: + string = "WSAEHOSTDOWN"; + description = "Host is down"; + break; + case WSAEHOSTUNREACH: + string = "WSAEHOSTUNREACH"; + description = "Host is unreachable"; + break; + case WSAHOST_NOT_FOUND: + string = "WSAHOST_NOT_FOUND"; + description = "No such host is known"; + break; + } + if (description_result) { + *description_result = description; + } + return string; +} + +void ReportWSAError(LPCSTR context, int error, const sockaddr_in& addr) { + talk_base::SocketAddress address; + address.FromSockAddr(addr); + LPCSTR description_string; + LPCSTR error_string = WSAErrorToString(error, &description_string); + LOG(LS_INFO) << context << " = " << error + << " (" << error_string << ":" << description_string << ") [" + << address.ToString() << "]"; +} +#else +void ReportWSAError(LPCSTR context, int error, const sockaddr_in& addr) { } +#endif + +///////////////////////////////////////////////////////////////////////////// +// Win32Socket::EventSink +///////////////////////////////////////////////////////////////////////////// + +#define WM_SOCKETNOTIFY (WM_USER + 50) +#define WM_DNSNOTIFY (WM_USER + 51) + +struct Win32Socket::DnsLookup { + HANDLE handle; + uint16 port; + char buffer[MAXGETHOSTSTRUCT]; +}; + +class Win32Socket::EventSink : public Win32Window { +public: + EventSink(Win32Socket * parent) : parent_(parent) { } + + void Dispose(); + + virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result); + virtual void OnFinalMessage(HWND hWnd); + +private: + bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result); + bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result); + + Win32Socket * parent_; +}; + +void +Win32Socket::EventSink::Dispose() { + parent_ = NULL; + if (::IsWindow(handle())) { + ::DestroyWindow(handle()); + } else { + delete this; + } +} + +bool Win32Socket::EventSink::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result) { + switch (uMsg) { + case WM_SOCKETNOTIFY: + case WM_TIMER: + return OnSocketNotify(uMsg, wParam, lParam, result); + case WM_DNSNOTIFY: + return OnDnsNotify(wParam, lParam, result); + } + return false; +} + +bool +Win32Socket::EventSink::OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result) { + result = 0; + + // Make sure the socket isn't already closed + if (!parent_ || (parent_->socket_ == INVALID_SOCKET)) + return true; + + int event = WSAGETSELECTEVENT(lParam); + int wsa_error = WSAGETSELECTERROR(lParam); + + if (uMsg == WM_TIMER) { + event = FD_CLOSE; + wsa_error = WSAETIMEDOUT; + } else if (event == FD_CLOSE) { + char ch; + if (::recv(parent_->socket_, &ch, 1, MSG_PEEK) > 0) { + parent_->signal_close_ = true; + return true; + } + } + + parent_->OnSocketNotify(event, wsa_error); + return true; +} + +bool +Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, LPARAM lParam, + LRESULT& result) { + result = 0; + + if (!parent_) + return true; + + if (!parent_->dns_ || + (parent_->dns_->handle != reinterpret_cast<HANDLE>(wParam))) { + ASSERT(false); + return true; + } + + uint32 ip = 0; + int error = WSAGETASYNCERROR(lParam); + + if (error == 0) { + hostent * pHost = reinterpret_cast<hostent *>(parent_->dns_->buffer); + uint32 net_ip = *reinterpret_cast<uint32 *>(pHost->h_addr_list[0]); + ip = talk_base::NetworkToHost32(net_ip); + } + + parent_->OnDnsNotify(ip, error); + return true; +} + +void +Win32Socket::EventSink::OnFinalMessage(HWND hWnd) { + delete this; +} + +///////////////////////////////////////////////////////////////////////////// +// Win32Socket +///////////////////////////////////////////////////////////////////////////// + +Win32Socket::Win32Socket() + : socket_(INVALID_SOCKET), error_(0), state_(CS_CLOSED), + signal_close_(false), sink_(NULL), dns_(NULL) { + // TODO: replace addr_ with SocketAddress + memset(&addr_, 0, sizeof(addr_)); +} + +Win32Socket::~Win32Socket() { + Close(); +} + +int +Win32Socket::Attach(SOCKET s) { + ASSERT(socket_ == INVALID_SOCKET); + if (socket_ != INVALID_SOCKET) + return SOCKET_ERROR; + + ASSERT(s != INVALID_SOCKET); + if (s == INVALID_SOCKET) + return SOCKET_ERROR; + + socket_ = s; + state_ = CS_CONNECTED; + + if (!Create(FD_READ | FD_WRITE | FD_CLOSE)) + return SOCKET_ERROR; + + return 0; +} + +void +Win32Socket::SetTimeout(int ms) { + if (sink_) + ::SetTimer(sink_->handle(), 1, ms, 0); +} + +talk_base::SocketAddress +Win32Socket::GetLocalAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getsockname(socket_, (sockaddr*)&addr, &addrlen); + ASSERT(addrlen == sizeof(addr)); + talk_base::SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } else { + ASSERT(result >= 0); + } + return address; +} + +talk_base::SocketAddress +Win32Socket::GetRemoteAddress() const { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int result = ::getpeername(socket_, (sockaddr*)&addr, &addrlen); + ASSERT(addrlen == sizeof(addr)); + talk_base::SocketAddress address; + if (result >= 0) { + address.FromSockAddr(addr); + } else { + ASSERT(errno == ENOTCONN); + } + return address; +} + +int +Win32Socket::Bind(const talk_base::SocketAddress& addr) { + ASSERT(socket_ == INVALID_SOCKET); + if (socket_ != INVALID_SOCKET) + return SOCKET_ERROR; + + if (!Create(FD_ACCEPT | FD_CLOSE)) + return SOCKET_ERROR; + + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int err = ::bind(socket_, (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return err; +} + +int +Win32Socket::Connect(const talk_base::SocketAddress& addr) { + ASSERT(socket_ == INVALID_SOCKET); + if (socket_ != INVALID_SOCKET) + return SOCKET_ERROR; + + if (!Create(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE)) + return SOCKET_ERROR; + + if (!addr.IsUnresolved()) { + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + + // now connect + return DoConnect(saddr); + } + + LOG_F(LS_INFO) << "async dns lookup (" << addr.IPAsString() << ")"; + DnsLookup * dns = new DnsLookup; + dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY, + addr.IPAsString().c_str(), dns->buffer, sizeof(dns->buffer)); + + if (!dns->handle) { + LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError(); + delete dns; + UpdateLastError(); + Close(); + return SOCKET_ERROR; + } + + dns->port = addr.port(); + dns_ = dns; + state_ = CS_CONNECTING; + return 0; +} + +int +Win32Socket::DoConnect(const sockaddr_in& addr) { + connect_time_ = talk_base::GetMillisecondCount(); + int result = connect(socket_, (SOCKADDR*)&addr, sizeof(addr)); + if (result == SOCKET_ERROR) { + int code = WSAGetLastError(); + if (code != WSAEWOULDBLOCK) { + ReportWSAError("WSAAsync:connect", code, addr); + error_ = code; + Close(); + return SOCKET_ERROR; + } + } + addr_ = addr; + state_ = CS_CONNECTING; + return 0; +} + +void +Win32Socket::OnSocketNotify(int event, int error) { + error_ = error; + switch (event) { + case FD_CONNECT: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:connect notify", error, addr_); +#ifdef DEBUG + int32 duration = talk_base::TimeDiff(talk_base::GetMillisecondCount(), + connect_time_); + LOG(LS_INFO) << "WSAAsync:connect error (" << duration + << " ms), faking close"; +#endif + Close(); + // If you get an error connecting, close doesn't really do anything + // and it certainly doesn't send back any close notification, but + // we really only maintain a few states, so it is easiest to get + // back into a known state by pretending that a close happened, even + // though the connect event never did occur. + SignalCloseEvent(this, error); + } else { +#ifdef DEBUG + int32 duration = talk_base::TimeDiff(talk_base::GetMillisecondCount(), + connect_time_); + LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)"; +#endif + state_ = CS_CONNECTED; + SignalConnectEvent(this); + } + break; + + case FD_ACCEPT: + case FD_READ: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:read notify", error, addr_); + Close(); + } else { + SignalReadEvent(this); + } + break; + + case FD_WRITE: + if (error != ERROR_SUCCESS) { + ReportWSAError("WSAAsync:write notify", error, addr_); + Close(); + } else { + SignalWriteEvent(this); + } + break; + + case FD_CLOSE: + ReportWSAError("WSAAsync:close notify", error, addr_); + Close(); + SignalCloseEvent(this, error); + break; + } +} + +void +Win32Socket::OnDnsNotify(int ip, int error) { + LOG_F(LS_INFO) << "(" << talk_base::SocketAddress::IPToString(ip) + << ", " << error << ")"; + if (error == 0) { + talk_base::SocketAddress address(ip, dns_->port); + sockaddr_in addr; + address.ToSockAddr(&addr); + error = DoConnect(addr); + } else { + Close(); + } + + if (error) { + error_ = error; + SignalCloseEvent(this, error_); + } else { + delete dns_; + dns_ = NULL; + } +} + +int +Win32Socket::GetError() const { + return error_; +} + +void +Win32Socket::SetError(int error) { + error_ = error; +} + +Socket::ConnState +Win32Socket::GetState() const { + return state_; +} + +int +Win32Socket::SetOption(Option opt, int value) { + ASSERT(opt == OPT_DONTFRAGMENT); + value = (value == 0) ? 0 : 1; + return ::setsockopt(socket_, IPPROTO_IP, IP_DONTFRAGMENT, + reinterpret_cast<char*>(&value), sizeof(value)); +} + +int +Win32Socket::Send(const void *pv, size_t cb) { + int sent = ::send(socket_, reinterpret_cast<const char *>(pv), (int)cb, 0); + UpdateLastError(); + return sent; +} + +int +Win32Socket::SendTo(const void *pv, size_t cb, + const talk_base::SocketAddress& addr) { + sockaddr_in saddr; + addr.ToSockAddr(&saddr); + int sent = ::sendto(socket_, reinterpret_cast<const char *>(pv), (int)cb, 0, + (sockaddr*)&saddr, sizeof(saddr)); + UpdateLastError(); + return sent; +} + +int +Win32Socket::Recv(void *pv, size_t cb) { + int received = ::recv(socket_, (char *)pv, (int)cb, 0); + UpdateLastError(); + if (signal_close_ && (received > 0)) { + char ch; + if (::recv(socket_, &ch, 1, MSG_PEEK) <= 0) { + signal_close_ = false; + ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY, + WSAMAKESELECTREPLY(FD_CLOSE, 0), 0); + } + } + return received; +} + +int +Win32Socket::RecvFrom(void *pv, size_t cb, talk_base::SocketAddress *paddr) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + int received = ::recvfrom(socket_, (char *)pv, (int)cb, 0, (sockaddr*)&saddr, + &cbAddr); + UpdateLastError(); + if (received != SOCKET_ERROR) + paddr->FromSockAddr(saddr); + if (signal_close_ && (received > 0)) { + char ch; + if (::recv(socket_, &ch, 1, MSG_PEEK) <= 0) { + signal_close_ = false; + ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY, + WSAMAKESELECTREPLY(FD_CLOSE, 0), 0); + } + } + return received; +} + +int +Win32Socket::Listen(int backlog) { + int err = ::listen(socket_, backlog); + UpdateLastError(); + if (err == 0) + state_ = CS_CONNECTING; + return err; +} + +talk_base::Socket* +Win32Socket::Accept(talk_base::SocketAddress *paddr) { + sockaddr_in saddr; + socklen_t cbAddr = sizeof(saddr); + SOCKET s = ::accept(socket_, (sockaddr*)&saddr, &cbAddr); + UpdateLastError(); + if (s == INVALID_SOCKET) + return NULL; + if (paddr) + paddr->FromSockAddr(saddr); + Win32Socket* socket = new Win32Socket; + if (0 == socket->Attach(s)) + return socket; + delete socket; + return NULL; +} + +int +Win32Socket::Close() { + int err = 0; + if (socket_ != INVALID_SOCKET) { + err = ::closesocket(socket_); + socket_ = INVALID_SOCKET; + signal_close_ = false; + UpdateLastError(); + } + if (dns_) { + WSACancelAsyncRequest(dns_->handle); + delete dns_; + dns_ = NULL; + } + if (sink_) { + sink_->Dispose(); + sink_ = NULL; + } + memset(&addr_, 0, sizeof(addr_)); // no longer connected, zero ip/port + state_ = CS_CLOSED; + return err; +} + +int +Win32Socket::EstimateMTU(uint16* mtu) { + talk_base::SocketAddress addr = GetRemoteAddress(); + if (addr.IsAny()) { + error_ = ENOTCONN; + return -1; + } + + talk_base::WinPing ping; + if (!ping.IsValid()) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + + for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) { + int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE; + talk_base::WinPing::PingResult result = + ping.Ping(addr.ip(), size, 0, 1, false); + if (result == talk_base::WinPing::PING_FAIL) { + error_ = EINVAL; // can't think of a better error ID + return -1; + } + if (result != talk_base::WinPing::PING_TOO_LARGE) { + *mtu = PACKET_MAXIMUMS[level]; + return 0; + } + } + + ASSERT(false); + return 0; +} + +bool +Win32Socket::Create(long events) { + ASSERT(NULL == sink_); + + if (INVALID_SOCKET == socket_) { + socket_ = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, 0); + if (socket_ == INVALID_SOCKET) { + UpdateLastError(); + return false; + } + } + + // Create window + sink_ = new EventSink(this); + sink_->Create(NULL, L"EventSink", 0, 0, 0, 0, 10, 10); + + // start the async select + if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events) + == SOCKET_ERROR) { + UpdateLastError(); + Close(); + return false; + } + + return true; +} + +void +Win32Socket::UpdateLastError() { + error_ = WSAGetLastError(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Win32SocketServer +/////////////////////////////////////////////////////////////////////////////// + +static UINT s_wm_wakeup_id; + +LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp); + +// A socket server that provides cricket base services on top of a win32 gui thread + +Win32SocketServer::Win32SocketServer(MessageQueue *message_queue) { + if (s_wm_wakeup_id == 0) + s_wm_wakeup_id = RegisterWindowMessage(L"WM_WAKEUP"); + message_queue_ = message_queue; + hwnd_ = NULL; + CreateDummyWindow(); +} + +Win32SocketServer::~Win32SocketServer() { + if (hwnd_ != NULL) { + KillTimer(hwnd_, 1); + ::DestroyWindow(hwnd_); + } +} + +Socket* Win32SocketServer::CreateSocket(int type) { + ASSERT(SOCK_STREAM == type); + return new Win32Socket; +} + +AsyncSocket* Win32SocketServer::CreateAsyncSocket(int type) { + ASSERT(SOCK_STREAM == type); + return new Win32Socket; +} + +bool Win32SocketServer::Wait(int cms, bool process_io) { + ASSERT(!process_io || (cms == 0)); // Should only be used for Thread::Send, or in Pump, below + if (cms == -1) { + MSG msg; + GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id); + } else if (cms != 0) { + Sleep(cms); + } + return true; +} + +void Win32SocketServer::WakeUp() { + // Always post for every wakeup, so there are no + // critical sections + if (hwnd_ != NULL) + PostMessage(hwnd_, s_wm_wakeup_id, 0, 0); +} + +void Win32SocketServer::Pump() { + // Process messages + Message msg; + while (message_queue_->Get(&msg, 0)) + message_queue_->Dispatch(&msg); + + // Anything remaining? + int delay = message_queue_->GetDelay(); + if (delay == -1) { + KillTimer(hwnd_, 1); + } else { + SetTimer(hwnd_, 1, delay, NULL); + } +} + +void Win32SocketServer::CreateDummyWindow() +{ + static bool s_registered; + if (!s_registered) { + ::WNDCLASSW wc; + memset(&wc, 0, sizeof(wc)); + wc.cbWndExtra = sizeof(this); + wc.lpszClassName = L"Dummy"; + wc.lpfnWndProc = DummyWndProc; + ::RegisterClassW(&wc); + s_registered = true; + } + + hwnd_ = ::CreateWindowW(L"Dummy", L"", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + SetWindowLong(hwnd_, GWL_USERDATA, (LONG)(LONG_PTR)this); +} + +LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp) +{ + if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) { + Win32SocketServer *ss = (Win32SocketServer *)(LONG_PTR)GetWindowLong(hwnd, GWL_USERDATA); + ss->Pump(); + return 0; + } + return ::DefWindowProc(hwnd, wm, wp, lp); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/win32socketserver.h b/Plugins/jingle/libjingle/talk/base/win32socketserver.h new file mode 100644 index 0000000..770141a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32socketserver.h @@ -0,0 +1,124 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_WIN32SOCKETSERVER_H__ +#define TALK_BASE_WIN32SOCKETSERVER_H__ + +#ifdef WIN32 + +#include "talk/base/messagequeue.h" +#include "talk/base/socketserver.h" +#include "talk/base/socketfactory.h" +#include "talk/base/socket.h" +#include "talk/base/asyncsocket.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Socket +/////////////////////////////////////////////////////////////////////////////// + +class Win32Socket : public talk_base::AsyncSocket { +public: + Win32Socket(); + virtual ~Win32Socket(); + + int Attach(SOCKET s); + void SetTimeout(int ms); + + // AsyncSocket Interface + virtual SocketAddress GetLocalAddress() const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Bind(const SocketAddress& addr); + virtual int Connect(const SocketAddress& addr); + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Recv(void *pv, size_t cb); + virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr); + virtual int Listen(int backlog); + virtual Socket *Accept(SocketAddress *paddr); + virtual int Close(); + virtual int GetError() const; + virtual void SetError(int error); + virtual ConnState GetState() const; + virtual int EstimateMTU(uint16* mtu); + virtual int SetOption(Option opt, int value); + +private: + bool Create(long events); + void UpdateLastError(); + + int DoConnect(const sockaddr_in& addr); + void OnSocketNotify(int event, int error); + void OnDnsNotify(int ip, int error); + + sockaddr_in addr_; // address that we connected to (see DoConnect) + SOCKET socket_; + int error_; + uint32 connect_time_; + ConnState state_; + bool signal_close_; + + class EventSink; + friend class EventSink; + EventSink * sink_; + + struct DnsLookup; + DnsLookup * dns_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Win32SocketServer +/////////////////////////////////////////////////////////////////////////////// + +class Win32SocketServer : public SocketServer { +public: + Win32SocketServer(MessageQueue *message_queue); + virtual ~Win32SocketServer(); + + // SocketServer Interface + virtual Socket* CreateSocket(int type); + virtual AsyncSocket* CreateAsyncSocket(int type); + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + void Pump(); + +private: + void CreateDummyWindow(); + + MessageQueue *message_queue_; + HWND hwnd_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // WIN32 + +#endif // TALK_BASE_WIN32SOCKETSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/base/win32window.h b/Plugins/jingle/libjingle/talk/base/win32window.h new file mode 100644 index 0000000..a4cdb47 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/win32window.h @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_WIN32WINDOW_H__ +#define TALK_BASE_WIN32WINDOW_H__ + +#ifdef WIN32 + +#include "talk/base/win32.h" + +namespace talk_base { + +/////////////////////////////////////////////////////////////////////////////// +// Win32Window +/////////////////////////////////////////////////////////////////////////////// + +class Win32Window { +public: + Win32Window(); + virtual ~Win32Window(); + + HWND handle() { return wnd_; } + + bool Create(HWND parent, const wchar_t* title, DWORD style, DWORD exstyle, + int x, int y, int cx, int cy); + void Destroy(); + +protected: + virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, + LRESULT& result); + + virtual bool OnClose() { return true; } + virtual void OnDestroyed() { } + +private: + static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); + + HWND wnd_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // WIN32 + +#endif // TALK_BASE_WIN32WINDOW_H__ diff --git a/Plugins/jingle/libjingle/talk/base/winfirewall.cc b/Plugins/jingle/libjingle/talk/base/winfirewall.cc new file mode 100644 index 0000000..9323d01 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/winfirewall.cc @@ -0,0 +1,141 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <comdef.h> +#include "netfw.h" +#include "winfirewall.h" + +#define RELEASE(lpUnk) do \ + { if ((lpUnk) != NULL) { (lpUnk)->Release(); (lpUnk) = NULL; } } while (0) + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// WinFirewall +////////////////////////////////////////////////////////////////////// + +WinFirewall::WinFirewall() : mgr_(NULL), policy_(NULL), profile_(NULL) { +} + +WinFirewall::~WinFirewall() { + Shutdown(); +} + +bool +WinFirewall::Initialize() { + if (mgr_) + return true; + + HRESULT hr = CoCreateInstance(__uuidof(NetFwMgr), + 0, CLSCTX_INPROC_SERVER, + __uuidof(INetFwMgr), + reinterpret_cast<void **>(&mgr_)); + if (SUCCEEDED(hr) && (mgr_ != NULL)) + hr = mgr_->get_LocalPolicy(&policy_); + if (SUCCEEDED(hr) && (policy_ != NULL)) + hr = policy_->get_CurrentProfile(&profile_); + return SUCCEEDED(hr) && (profile_ != NULL); +} + +void +WinFirewall::Shutdown() { + RELEASE(profile_); + RELEASE(policy_); + RELEASE(mgr_); +} + +bool +WinFirewall::Enabled() { + if (!profile_) + return false; + + VARIANT_BOOL fwEnabled = VARIANT_FALSE; + profile_->get_FirewallEnabled(&fwEnabled); + return (fwEnabled != VARIANT_FALSE); +} + +bool +WinFirewall::Authorized(const char * filename, bool * known) { + if (known) { + *known = false; + } + + if (!profile_) + return false; + + VARIANT_BOOL fwEnabled = VARIANT_FALSE; + _bstr_t bfilename = filename; + + INetFwAuthorizedApplications * apps = NULL; + HRESULT hr = profile_->get_AuthorizedApplications(&apps); + if (SUCCEEDED(hr) && (apps != NULL)) { + INetFwAuthorizedApplication * app = NULL; + hr = apps->Item(bfilename, &app); + if (SUCCEEDED(hr) && (app != NULL)) { + hr = app->get_Enabled(&fwEnabled); + app->Release(); + if (known) { + *known = true; + } + } else if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { + // Unexpected error + } + apps->Release(); + } + + return (fwEnabled != VARIANT_FALSE); +} + +bool +WinFirewall::AddApplication(const char * filename, const char * friendly_name, + bool authorized) { + INetFwAuthorizedApplications * apps = NULL; + HRESULT hr = profile_->get_AuthorizedApplications(&apps); + if (SUCCEEDED(hr) && (apps != NULL)) { + INetFwAuthorizedApplication * app = NULL; + hr = CoCreateInstance(__uuidof(NetFwAuthorizedApplication), + 0, CLSCTX_INPROC_SERVER, + __uuidof(INetFwAuthorizedApplication), + reinterpret_cast<void **>(&app)); + if (SUCCEEDED(hr) && (app != NULL)) { + _bstr_t bstr = filename; + hr = app->put_ProcessImageFileName(bstr); + bstr = friendly_name; + if (SUCCEEDED(hr)) + hr = app->put_Name(bstr); + if (SUCCEEDED(hr)) + hr = app->put_Enabled(authorized ? VARIANT_TRUE : VARIANT_FALSE); + if (SUCCEEDED(hr)) + hr = apps->Add(app); + app->Release(); + } + apps->Release(); + } + return SUCCEEDED(hr); +} + +} // namespace talk_base diff --git a/Plugins/jingle/libjingle/talk/base/winfirewall.h b/Plugins/jingle/libjingle/talk/base/winfirewall.h new file mode 100644 index 0000000..09d5a2f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/winfirewall.h @@ -0,0 +1,64 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_WINFIREWALL_H__ +#define TALK_BASE_WINFIREWALL_H__ + +struct INetFwMgr; +struct INetFwPolicy; +struct INetFwProfile; + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// WinFirewall +////////////////////////////////////////////////////////////////////// + +class WinFirewall { +public: + WinFirewall(); + ~WinFirewall(); + + bool Initialize(); + void Shutdown(); + + bool Enabled(); + bool Authorized(const char * filename, bool * known = 0); + + bool AddApplication(const char * filename, const char * friendly_name, bool authorized = true); + +private: + INetFwMgr * mgr_; + INetFwPolicy * policy_; + INetFwProfile * profile_; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base + +#endif // TALK_BASE_WINFIREWALL_H__ diff --git a/Plugins/jingle/libjingle/talk/base/winping.cc b/Plugins/jingle/libjingle/talk/base/winping.cc new file mode 100644 index 0000000..401a1b2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/winping.cc @@ -0,0 +1,317 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/byteorder.h" +#include "talk/base/socketaddress.h" +#include "talk/base/winping.h" +#include "talk/base/logging.h" +#include <cassert> + +namespace talk_base { + +////////////////////////////////////////////////////////////////////// +// Found in IPExport.h +////////////////////////////////////////////////////////////////////// + +typedef struct icmp_echo_reply { + ULONG Address; // Replying address + ULONG Status; // Reply IP_STATUS + ULONG RoundTripTime; // RTT in milliseconds + USHORT DataSize; // Reply data size in bytes + USHORT Reserved; // Reserved for system use + PVOID Data; // Pointer to the reply data + struct ip_option_information Options; // Reply options +} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY; + +// +// IP_STATUS codes returned from IP APIs +// + +#define IP_STATUS_BASE 11000 + +#define IP_SUCCESS 0 +#define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1) +#define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2) +#define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3) +#define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4) +#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) +#define IP_NO_RESOURCES (IP_STATUS_BASE + 6) +#define IP_BAD_OPTION (IP_STATUS_BASE + 7) +#define IP_HW_ERROR (IP_STATUS_BASE + 8) +#define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9) +#define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10) +#define IP_BAD_REQ (IP_STATUS_BASE + 11) +#define IP_BAD_ROUTE (IP_STATUS_BASE + 12) +#define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13) +#define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14) +#define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15) +#define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16) +#define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17) +#define IP_BAD_DESTINATION (IP_STATUS_BASE + 18) + +#define IP_ADDR_DELETED (IP_STATUS_BASE + 19) +#define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20) +#define IP_MTU_CHANGE (IP_STATUS_BASE + 21) +#define IP_UNLOAD (IP_STATUS_BASE + 22) +#define IP_ADDR_ADDED (IP_STATUS_BASE + 23) +#define IP_MEDIA_CONNECT (IP_STATUS_BASE + 24) +#define IP_MEDIA_DISCONNECT (IP_STATUS_BASE + 25) +#define IP_BIND_ADAPTER (IP_STATUS_BASE + 26) +#define IP_UNBIND_ADAPTER (IP_STATUS_BASE + 27) +#define IP_DEVICE_DOES_NOT_EXIST (IP_STATUS_BASE + 28) +#define IP_DUPLICATE_ADDRESS (IP_STATUS_BASE + 29) +#define IP_INTERFACE_METRIC_CHANGE (IP_STATUS_BASE + 30) +#define IP_RECONFIG_SECFLTR (IP_STATUS_BASE + 31) +#define IP_NEGOTIATING_IPSEC (IP_STATUS_BASE + 32) +#define IP_INTERFACE_WOL_CAPABILITY_CHANGE (IP_STATUS_BASE + 33) +#define IP_DUPLICATE_IPADD (IP_STATUS_BASE + 34) + +#define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50) +#define MAX_IP_STATUS IP_GENERAL_FAILURE +#define IP_PENDING (IP_STATUS_BASE + 255) + +// +// Values used in the IP header Flags field. +// +#define IP_FLAG_DF 0x2 // Don't fragment this packet. + +// +// Supported IP Option Types. +// +// These types define the options which may be used in the OptionsData field +// of the ip_option_information structure. See RFC 791 for a complete +// description of each. +// +#define IP_OPT_EOL 0 // End of list option +#define IP_OPT_NOP 1 // No operation +#define IP_OPT_SECURITY 0x82 // Security option +#define IP_OPT_LSRR 0x83 // Loose source route +#define IP_OPT_SSRR 0x89 // Strict source route +#define IP_OPT_RR 0x7 // Record route +#define IP_OPT_TS 0x44 // Timestamp +#define IP_OPT_SID 0x88 // Stream ID (obsolete) +#define IP_OPT_ROUTER_ALERT 0x94 // Router Alert Option + +#define MAX_OPT_SIZE 40 // Maximum length of IP options in bytes + +////////////////////////////////////////////////////////////////////// +// Global Constants and Types +////////////////////////////////////////////////////////////////////// + +const char * const ICMP_DLL_NAME = "icmp.dll"; +const char * const ICMP_CREATE_FUNC = "IcmpCreateFile"; +const char * const ICMP_CLOSE_FUNC = "IcmpCloseHandle"; +const char * const ICMP_SEND_FUNC = "IcmpSendEcho"; + +inline uint32 ReplySize(uint32 data_size) { + // A ping error message is 8 bytes long, so make sure we allow for at least + // 8 bytes of reply data. + return sizeof(ICMP_ECHO_REPLY) + max(8UL, data_size); +} + +////////////////////////////////////////////////////////////////////// +// WinPing +////////////////////////////////////////////////////////////////////// + +WinPing::WinPing() + : dll_(0), hping_(INVALID_HANDLE_VALUE), create_(0), close_(0), send_(0), + data_(0), dlen_(0), reply_(0), rlen_(0), valid_(false) { + + dll_ = LoadLibraryA(ICMP_DLL_NAME); + if (!dll_) { + LOG(LERROR) << "LoadLibrary: " << GetLastError(); + return; + } + + create_ = (PIcmpCreateFile) GetProcAddress(dll_, ICMP_CREATE_FUNC); + close_ = (PIcmpCloseHandle) GetProcAddress(dll_, ICMP_CLOSE_FUNC); + send_ = (PIcmpSendEcho) GetProcAddress(dll_, ICMP_SEND_FUNC); + if (!create_ || !close_ || !send_) { + LOG(LERROR) << "GetProcAddress(ICMP_*): " << GetLastError(); + return; + } + + hping_ = create_(); + if (hping_ == INVALID_HANDLE_VALUE) { + LOG(LERROR) << "IcmpCreateFile: " << GetLastError(); + return; + } + + dlen_ = 0; + rlen_ = ReplySize(dlen_); + data_ = new char[dlen_]; + reply_ = new char[rlen_]; + + valid_ = true; +} + +WinPing::~WinPing() { + if (dll_) + FreeLibrary(dll_); + + if ((hping_ != INVALID_HANDLE_VALUE) && close_) { + if (!close_(hping_)) + LOG(WARNING) << "IcmpCloseHandle: " << GetLastError(); + } + + delete[] data_; + delete reply_; +} + +WinPing::PingResult WinPing::Ping( + uint32 ip, uint32 data_size, uint32 timeout, uint8 ttl, + bool allow_fragments) { + + assert(IsValid()); + + IP_OPTION_INFORMATION ipopt; + memset(&ipopt, 0, sizeof(ipopt)); + if (!allow_fragments) + ipopt.Flags |= IP_FLAG_DF; + ipopt.Ttl = ttl; + + uint32 reply_size = ReplySize(data_size); + + if (data_size > dlen_) { + delete [] data_; + dlen_ = data_size; + data_ = new char[dlen_]; + memset(data_, 'z', dlen_); + } + + if (reply_size > rlen_) { + delete [] reply_; + rlen_ = reply_size; + reply_ = new char[rlen_]; + } + + DWORD result = send_(hping_, talk_base::HostToNetwork32(ip), + data_, uint16(data_size), &ipopt, + reply_, reply_size, timeout); + if (result == 0) { + long error = GetLastError(); + if (error == IP_PACKET_TOO_BIG) + return PING_TOO_LARGE; + if (error == IP_REQ_TIMED_OUT) + return PING_TIMEOUT; + LOG(LERROR) << "IcmpSendEcho(" << talk_base::SocketAddress::IPToString(ip) + << ", " << data_size << "): " << error; + return PING_FAIL; + } + + return PING_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// +// Microsoft Documenation +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpCreateFile +// +// Routine Description: +// +// Opens a handle on which ICMP Echo Requests can be issued. +// +// Arguments: +// +// None. +// +// Return Value: +// +// An open file handle or INVALID_HANDLE_VALUE. Extended error information +// is available by calling GetLastError(). +// +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpCloseHandle +// +// Routine Description: +// +// Closes a handle opened by ICMPOpenFile. +// +// Arguments: +// +// IcmpHandle - The handle to close. +// +// Return Value: +// +// TRUE if the handle was closed successfully, otherwise FALSE. Extended +// error information is available by calling GetLastError(). +// +////////////////////////////////////////////////////////////////////// +// +// Routine Name: +// +// IcmpSendEcho +// +// Routine Description: +// +// Sends an ICMP Echo request and returns any replies. The +// call returns when the timeout has expired or the reply buffer +// is filled. +// +// Arguments: +// +// IcmpHandle - An open handle returned by ICMPCreateFile. +// +// DestinationAddress - The destination of the echo request. +// +// RequestData - A buffer containing the data to send in the +// request. +// +// RequestSize - The number of bytes in the request data buffer. +// +// RequestOptions - Pointer to the IP header options for the request. +// May be NULL. +// +// ReplyBuffer - A buffer to hold any replies to the request. +// On return, the buffer will contain an array of +// ICMP_ECHO_REPLY structures followed by the +// options and data for the replies. The buffer +// should be large enough to hold at least one +// ICMP_ECHO_REPLY structure plus +// MAX(RequestSize, 8) bytes of data since an ICMP +// error message contains 8 bytes of data. +// +// ReplySize - The size in bytes of the reply buffer. +// +// Timeout - The time in milliseconds to wait for replies. +// +// Return Value: +// +// Returns the number of ICMP_ECHO_REPLY structures stored in ReplyBuffer. +// The status of each reply is contained in the structure. If the return +// value is zero, extended error information is available via +// GetLastError(). +// +////////////////////////////////////////////////////////////////////// + +} // namespace talk_base
\ No newline at end of file diff --git a/Plugins/jingle/libjingle/talk/base/winping.h b/Plugins/jingle/libjingle/talk/base/winping.h new file mode 100644 index 0000000..0d0b051 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/base/winping.h @@ -0,0 +1,105 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_BASE_WINPING_H__ +#define TALK_BASE_WINPING_H__ + +#ifdef WIN32 + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include <winsock2.h> +#define _WINSOCKAPI_ +#include <windows.h> +#undef SetPort + +#include "talk/base/basictypes.h" + +namespace talk_base { + +// This class wraps a Win32 API for doing ICMP pinging. This API, unlike the +// the normal socket APIs (as implemented on Win9x), will return an error if +// an ICMP packet with the dont-fragment bit set is too large. This means this +// class can be used to detect the MTU to a given address. + +typedef struct ip_option_information { + UCHAR Ttl; // Time To Live + UCHAR Tos; // Type Of Service + UCHAR Flags; // IP header flags + UCHAR OptionsSize; // Size in bytes of options data + PUCHAR OptionsData; // Pointer to options data +} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION; + +typedef HANDLE (WINAPI *PIcmpCreateFile)(); + +typedef BOOL (WINAPI *PIcmpCloseHandle)(HANDLE icmp_handle); + +typedef DWORD (WINAPI *PIcmpSendEcho)( + HANDLE IcmpHandle, + ULONG DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout); + +class WinPing { +public: + WinPing(); + ~WinPing(); + + // Determines whether the class was initialized correctly. + bool IsValid() { return valid_; } + + // Attempts to send a ping with the given parameters. + enum PingResult { PING_FAIL, PING_TOO_LARGE, PING_TIMEOUT, PING_SUCCESS }; + PingResult Ping( + uint32 ip, uint32 data_size, uint32 timeout_millis, uint8 ttl, + bool allow_fragments); + +private: + HMODULE dll_; + HANDLE hping_; + PIcmpCreateFile create_; + PIcmpCloseHandle close_; + PIcmpSendEcho send_; + char* data_; + uint32 dlen_; + char* reply_; + uint32 rlen_; + bool valid_; +}; + +} // namespace talk_base + +#endif // WIN32 + +#endif // TALK_BASE_WINPING_H__ + diff --git a/Plugins/jingle/libjingle/talk/examples/call/Makefile.am b/Plugins/jingle/libjingle/talk/examples/call/Makefile.am new file mode 100644 index 0000000..30a3dc6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST=call.vcproj +bin_PROGRAMS = call +call_CXXFLAGS = $(AM_CXXFLAGS) +call_SOURCES = call_main.cc callclient.cc console.cc +noinst_HEADERS = callclient.h console.h +call_LDADD = \ + $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread -lssl -lcrypto $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) $(MEDIA_LIBS) +AM_CPPFLAGS = -DPOSIX diff --git a/Plugins/jingle/libjingle/talk/examples/call/Makefile.in b/Plugins/jingle/libjingle/talk/examples/call/Makefile.in new file mode 100644 index 0000000..591d3aa --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/Makefile.in @@ -0,0 +1,537 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = call$(EXEEXT) +subdir = talk/examples/call +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_call_OBJECTS = call-call_main.$(OBJEXT) call-callclient.$(OBJEXT) \ + call-console.$(OBJEXT) +call_OBJECTS = $(am_call_OBJECTS) +am__DEPENDENCIES_1 = +call_DEPENDENCIES = \ + $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(call_SOURCES) +DIST_SOURCES = $(call_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +EXTRA_DIST = call.vcproj +call_CXXFLAGS = $(AM_CXXFLAGS) +call_SOURCES = call_main.cc callclient.cc console.cc +noinst_HEADERS = callclient.h console.h +call_LDADD = \ + $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/phone/libcricketsessionphone.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread -lssl -lcrypto $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) $(MEDIA_LIBS) + +AM_CPPFLAGS = -DPOSIX +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/call/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/examples/call/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +call$(EXEEXT): $(call_OBJECTS) $(call_DEPENDENCIES) + @rm -f call$(EXEEXT) + $(CXXLINK) $(call_LDFLAGS) $(call_OBJECTS) $(call_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-call_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-callclient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call-console.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +call-call_main.o: call_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-call_main.o -MD -MP -MF "$(DEPDIR)/call-call_main.Tpo" -c -o call-call_main.o `test -f 'call_main.cc' || echo '$(srcdir)/'`call_main.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-call_main.Tpo" "$(DEPDIR)/call-call_main.Po"; else rm -f "$(DEPDIR)/call-call_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='call_main.cc' object='call-call_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-call_main.o `test -f 'call_main.cc' || echo '$(srcdir)/'`call_main.cc + +call-call_main.obj: call_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-call_main.obj -MD -MP -MF "$(DEPDIR)/call-call_main.Tpo" -c -o call-call_main.obj `if test -f 'call_main.cc'; then $(CYGPATH_W) 'call_main.cc'; else $(CYGPATH_W) '$(srcdir)/call_main.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-call_main.Tpo" "$(DEPDIR)/call-call_main.Po"; else rm -f "$(DEPDIR)/call-call_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='call_main.cc' object='call-call_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-call_main.obj `if test -f 'call_main.cc'; then $(CYGPATH_W) 'call_main.cc'; else $(CYGPATH_W) '$(srcdir)/call_main.cc'; fi` + +call-callclient.o: callclient.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-callclient.o -MD -MP -MF "$(DEPDIR)/call-callclient.Tpo" -c -o call-callclient.o `test -f 'callclient.cc' || echo '$(srcdir)/'`callclient.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-callclient.Tpo" "$(DEPDIR)/call-callclient.Po"; else rm -f "$(DEPDIR)/call-callclient.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='callclient.cc' object='call-callclient.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-callclient.o `test -f 'callclient.cc' || echo '$(srcdir)/'`callclient.cc + +call-callclient.obj: callclient.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-callclient.obj -MD -MP -MF "$(DEPDIR)/call-callclient.Tpo" -c -o call-callclient.obj `if test -f 'callclient.cc'; then $(CYGPATH_W) 'callclient.cc'; else $(CYGPATH_W) '$(srcdir)/callclient.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-callclient.Tpo" "$(DEPDIR)/call-callclient.Po"; else rm -f "$(DEPDIR)/call-callclient.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='callclient.cc' object='call-callclient.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-callclient.obj `if test -f 'callclient.cc'; then $(CYGPATH_W) 'callclient.cc'; else $(CYGPATH_W) '$(srcdir)/callclient.cc'; fi` + +call-console.o: console.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-console.o -MD -MP -MF "$(DEPDIR)/call-console.Tpo" -c -o call-console.o `test -f 'console.cc' || echo '$(srcdir)/'`console.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-console.Tpo" "$(DEPDIR)/call-console.Po"; else rm -f "$(DEPDIR)/call-console.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='console.cc' object='call-console.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-console.o `test -f 'console.cc' || echo '$(srcdir)/'`console.cc + +call-console.obj: console.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -MT call-console.obj -MD -MP -MF "$(DEPDIR)/call-console.Tpo" -c -o call-console.obj `if test -f 'console.cc'; then $(CYGPATH_W) 'console.cc'; else $(CYGPATH_W) '$(srcdir)/console.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/call-console.Tpo" "$(DEPDIR)/call-console.Po"; else rm -f "$(DEPDIR)/call-console.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='console.cc' object='call-console.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(call_CXXFLAGS) $(CXXFLAGS) -c -o call-console.obj `if test -f 'console.cc'; then $(CYGPATH_W) 'console.cc'; else $(CYGPATH_W) '$(srcdir)/console.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/examples/call/call.vcproj b/Plugins/jingle/libjingle/talk/examples/call/call.vcproj new file mode 100644 index 0000000..3268f2c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/call.vcproj @@ -0,0 +1,262 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="call"
+ ProjectGUID="{A4132D45-BAE2-40E5-AC7C-C3C44FB24325}"
+ RootNamespace="call"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib crypt32.lib mediastreamer2.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\Desenvolvimento\libjingle-0.4.0\talk\third_party\Expat\StaticLibs;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\build\win32native\Debug"
+ IgnoreAllDefaultLibraries="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib "$(SolutionDir)\third_party\gips\Library\gipsvoiceenginelite.lib""
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\call_main.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\callclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\console.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\jingleinfotask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presenceouttask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presencepushtask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppauth.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmpppump.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppthread.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\callclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\console.h"
+ >
+ </File>
+ <File
+ RelativePath=".\presenceouttask.h"
+ >
+ </File>
+ <File
+ RelativePath=".\presencepushtask.h"
+ >
+ </File>
+ <File
+ RelativePath=".\status.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ 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>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/examples/call/call_main.cc b/Plugins/jingle/libjingle/talk/examples/call/call_main.cc new file mode 100644 index 0000000..989e3a2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/call_main.cc @@ -0,0 +1,256 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <time.h> +#include <iomanip> +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/ssladapter.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" + +#if defined(_MSC_VER) && (_MSC_VER < 1400) +// The following are necessary to properly link when compiling STL without +// /EHsc, otherwise known as C++ exceptions. +void __cdecl std::_Throw(const std::exception &) {} +std::_Prhand std::_Raise_handler = 0; +#endif + +void SetConsoleEcho(bool on) { +#ifdef WIN32 + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) + return; + + DWORD mode; + if (!GetConsoleMode(hIn, &mode)) + return; + + if (on) { + mode = mode | ENABLE_ECHO_INPUT; + } else { + mode = mode & ~ENABLE_ECHO_INPUT; + } + + SetConsoleMode(hIn, mode); +#else + if (on) + system("stty echo"); + else + system("stty -echo"); +#endif +} +class DebugLog : public sigslot::has_slots<> { +public: + DebugLog() : + debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0), + debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0), + censor_password_(false) + {} + char * debug_input_buf_; + int debug_input_len_; + int debug_input_alloc_; + char * debug_output_buf_; + int debug_output_len_; + int debug_output_alloc_; + bool censor_password_; + + void Input(const char * data, int len) { + if (debug_input_len_ + len > debug_input_alloc_) { + char * old_buf = debug_input_buf_; + debug_input_alloc_ = 4096; + while (debug_input_alloc_ < debug_input_len_ + len) { + debug_input_alloc_ *= 2; + } + debug_input_buf_ = new char[debug_input_alloc_]; + memcpy(debug_input_buf_, old_buf, debug_input_len_); + delete[] old_buf; + } + memcpy(debug_input_buf_ + debug_input_len_, data, len); + debug_input_len_ += len; + DebugPrint(debug_input_buf_, &debug_input_len_, false); + } + + void Output(const char * data, int len) { + if (debug_output_len_ + len > debug_output_alloc_) { + char * old_buf = debug_output_buf_; + debug_output_alloc_ = 4096; + while (debug_output_alloc_ < debug_output_len_ + len) { + debug_output_alloc_ *= 2; + } + debug_output_buf_ = new char[debug_output_alloc_]; + memcpy(debug_output_buf_, old_buf, debug_output_len_); + delete[] old_buf; + } + memcpy(debug_output_buf_ + debug_output_len_, data, len); + debug_output_len_ += len; + DebugPrint(debug_output_buf_, &debug_output_len_, true); + } + + static bool + IsAuthTag(const char * str, size_t len) { + if (str[0] == '<' && str[1] == 'a' && + str[2] == 'u' && + str[3] == 't' && + str[4] == 'h' && + str[5] <= ' ') { + std::string tag(str, len); + + if (tag.find("mechanism") != std::string::npos) + return true; + + } + return false; + } + + void + DebugPrint(char * buf, int * plen, bool output) { + int len = *plen; + if (len > 0) { + time_t tim = time(NULL); + struct tm * now = localtime(&tim); + char *time_string = asctime(now); + if (time_string) { + size_t time_len = strlen(time_string); + if (time_len > 0) { + time_string[time_len-1] = 0; // trim off terminating \n + } + } + LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<") + << " : " << time_string; + + bool indent; + int start = 0, nest = 3; + for (int i = 0; i < len; i += 1) { + if (buf[i] == '>') { + if ((i > 0) && (buf[i-1] == '/')) { + indent = false; + } else if ((start + 1 < len) && (buf[start + 1] == '/')) { + indent = false; + nest -= 2; + } else { + indent = true; + } + + // Output a tag + LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start); + + if (indent) + nest += 2; + + // Note if it's a PLAIN auth tag + if (IsAuthTag(buf + start, i + 1 - start)) { + censor_password_ = true; + } + + // incr + start = i + 1; + } + + if (buf[i] == '<' && start < i) { + if (censor_password_) { + LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##"; + censor_password_ = false; + } + else { + LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start); + } + start = i; + } + } + len = len - start; + memcpy(buf, buf + start, len); + *plen = len; + } + } + +}; + +static DebugLog debug_log_; + + +int main(int argc, char **argv) { + // This app has three threads. The main thread will run the XMPP client, + // which will print to the screen in its own thread. A second thread + // will get input from the console, parse it, and pass the appropriate + // message back to the XMPP client's thread. A third thread is used + // by PhoneSessionClient as its worker thread. + + bool debug = false; + if (argc > 1 && !strcmp(argv[1], "-d")) + debug = true; + + if (debug) + talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE); + + + talk_base::InitializeSSL(); + XmppPump pump; + buzz::Jid jid; + buzz::XmppClientSettings xcs; + talk_base::InsecureCryptStringImpl pass; + std::string username; + + std::cout << "JID: "; + std::cin >> username; + jid = buzz::Jid(username); + if (!jid.IsValid() || jid.node() == "") { + printf("Invalid JID. JIDs should be in the form user@domain\n"); + return 1; + } + SetConsoleEcho(false); + std::cout << "Password: "; + std::cin >> pass.password(); + SetConsoleEcho(true); + std::cout << std::endl; + + xcs.set_user(jid.node()); + xcs.set_resource("call"); + xcs.set_host(jid.domain()); + xcs.set_use_tls(true); + + xcs.set_pass(talk_base::CryptString(pass)); + xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222)); + printf("Logging in as %s\n", jid.Str().c_str()); + + talk_base::PhysicalSocketServer ss; + + CallClient *client = new CallClient(pump.client()); + + talk_base::Thread main_thread(&ss); + talk_base::ThreadManager::SetCurrent(&main_thread); + Console *console = new Console(&main_thread, client); + client->SetConsole(console); + talk_base::Thread *console_thread = new talk_base::Thread(&ss); + console_thread->Start(); + console_thread->Post(console, MSG_START); + + if (debug) { + pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input); + pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output); + } + + pump.DoLogin(xcs, new XmppSocket(true), NULL); + main_thread.Run(); + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/examples/call/callclient.cc b/Plugins/jingle/libjingle/talk/examples/call/callclient.cc new file mode 100644 index 0000000..38b535b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/callclient.cc @@ -0,0 +1,364 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> +#include <vector> + +#include "talk/xmpp/constants.h" +#include "talk/base/helpers.h" +#include "talk/base/thread.h" +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/client/httpportallocator.h" +#include "talk/p2p/client/sessionmanagertask.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/examples/call/callclient.h" +#include "talk/examples/call/console.h" +#include "talk/examples/login/presencepushtask.h" +#include "talk/examples/login/presenceouttask.h" +#include "talk/examples/login/jingleinfotask.h" + +namespace { + +const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) { + switch (show) { + case buzz::Status::SHOW_XA: return desc.c_str(); + case buzz::Status::SHOW_ONLINE: return "online"; + case buzz::Status::SHOW_AWAY: return "away"; + case buzz::Status::SHOW_DND: return "do not disturb"; + case buzz::Status::SHOW_CHAT: return "ready to chat"; + default: return "offline"; + } +} + +} // namespace + +const char* CALL_COMMANDS = +"Available commands:\n" +"\n" +" hangup Ends the call.\n" +" mute Stops sending voice.\n" +" unmute Re-starts sending voice.\n" +" quit Quits the application.\n" +""; + +const char* RECEIVE_COMMANDS = +"Available commands:\n" +"\n" +" accept Accepts the incoming call and switches to it.\n" +" reject Rejects the incoming call and stays with the current call.\n" +" quit Quits the application.\n" +""; + +const char* CONSOLE_COMMANDS = +"Available commands:\n" +"\n" +" roster Prints the online friends from your roster.\n" +" call <name> Initiates a call to the friend with the given name.\n" +" quit Quits the application.\n" +""; + +void CallClient::ParseLine(const std::string& line) { + std::vector<std::string> words; + int start = -1; + int state = 0; + for (int index = 0; index <= static_cast<int>(line.size()); ++index) { + if (state == 0) { + if (!isspace(line[index])) { + start = index; + state = 1; + } + } else { + assert(state == 1); + assert(start >= 0); + if (isspace(line[index])) { + std::string word(line, start, index - start); + words.push_back(word); + start = -1; + state = 0; + } + } + } + + // Global commands + if ((words.size() == 1) && (words[0] == "quit")) { + exit(0); + } + + if (call_ && incoming_call_) { + if ((words.size() == 1) && (words[0] == "accept")) { + assert(call_->sessions().size() == 1); + call_->AcceptSession(call_->sessions()[0]); + phone_client()->SetFocus(call_); + incoming_call_ = false; + } else if ((words.size() == 1) && (words[0] == "reject")) { + call_->RejectSession(call_->sessions()[0]); + incoming_call_ = false; + } else { + console_->Print(RECEIVE_COMMANDS); + } + } else if (call_) { + if ((words.size() == 1) && (words[0] == "hangup")) { + call_->Terminate(); + call_ = NULL; + session_ = NULL; + console_->SetPrompt(NULL); + } else if ((words.size() == 1) && (words[0] == "mute")) { + call_->Mute(true); + } else if ((words.size() == 1) && (words[0] == "unmute")) { + call_->Mute(false); + } else { + console_->Print(CALL_COMMANDS); + } + } else { + if ((words.size() == 1) && (words[0] == "roster")) { + PrintRoster(); + } else if ((words.size() == 2) && (words[0] == "call")) { + MakeCallTo(words[1]); + } else { + console_->Print(CONSOLE_COMMANDS); + } + } +} + +CallClient::CallClient(buzz::XmppClient* xmpp_client) + : xmpp_client_(xmpp_client), roster_(new RosterMap), call_(NULL), + incoming_call_(false) { + xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); +} + +CallClient::~CallClient() { + delete roster_; +} + +const std::string CallClient::strerror(buzz::XmppEngine::Error err) { + switch (err) { + case buzz::XmppEngine::ERROR_NONE: + return ""; + case buzz::XmppEngine::ERROR_XML: + return "Malformed XML or encoding error"; + case buzz::XmppEngine::ERROR_STREAM: + return "XMPP stream error"; + case buzz::XmppEngine::ERROR_VERSION: + return "XMPP version error"; + case buzz::XmppEngine::ERROR_UNAUTHORIZED: + return "User is not authorized (Check your username and password)"; + case buzz::XmppEngine::ERROR_TLS: + return "TLS could not be negotiated"; + case buzz::XmppEngine::ERROR_AUTH: + return "Authentication could not be negotiated"; + case buzz::XmppEngine::ERROR_BIND: + return "Resource or session binding could not be negotiated"; + case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: + return "Connection closed by output handler."; + case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: + return "Closed by </stream:stream>"; + case buzz::XmppEngine::ERROR_SOCKET: + return "Socket error"; + default: + return "Unknown error"; + } +} + +void CallClient::OnCallDestroy(cricket::Call* call) { + if (call == call_) { + console_->SetPrompt(NULL); + console_->Print("call destroyed"); + call_ = NULL; + session_ = NULL; + } +} + +void CallClient::OnJingleInfo(const std::string &relay_token, + const std::vector<std::string> &relay_addresses, + const std::vector<talk_base::SocketAddress> &stun_addresses) { + port_allocator_->SetStunHosts(stun_addresses); + port_allocator_->SetRelayHosts(relay_addresses); + port_allocator_->SetRelayToken(relay_token); +} + +void CallClient::OnStateChange(buzz::XmppEngine::State state) { + switch (state) { + case buzz::XmppEngine::STATE_START: + console_->Print("connecting..."); + break; + + case buzz::XmppEngine::STATE_OPENING: + console_->Print("logging in..."); + break; + + case buzz::XmppEngine::STATE_OPEN: + console_->Print("logged in..."); + InitPhone(); + InitPresence(); + break; + + case buzz::XmppEngine::STATE_CLOSED: + buzz::XmppEngine::Error error = xmpp_client_->GetError(NULL); + console_->Print("logged out..." + strerror(error)); + exit(0); + } +} + +void CallClient::InitPhone() { + std::string client_unique = xmpp_client_->jid().Str(); + cricket::InitRandom(client_unique.c_str(), client_unique.size()); + + worker_thread_ = new talk_base::Thread(); + + port_allocator_ = new cricket::HttpPortAllocator(&network_manager_, "call"); + + session_manager_ = new cricket::SessionManager( + port_allocator_, worker_thread_); + session_manager_->SignalRequestSignaling.connect( + this, &CallClient::OnRequestSignaling); + session_manager_->OnSignalingReady(); + + session_manager_task_ = + new cricket::SessionManagerTask(xmpp_client_, session_manager_); + session_manager_task_->EnableOutgoingMessages(); + session_manager_task_->Start(); + + buzz::JingleInfoTask *jit = new buzz::JingleInfoTask(xmpp_client_); + jit->RefreshJingleInfoNow(); + jit->SignalJingleInfo.connect(this, &CallClient::OnJingleInfo); + jit->Start(); + + phone_client_ = new cricket::PhoneSessionClient( + xmpp_client_->jid(),session_manager_); + phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); + + worker_thread_->Start(); +} + +void CallClient::OnRequestSignaling() { + session_manager_->OnSignalingReady(); +} + +void CallClient::OnCallCreate(cricket::Call* call) { + call->SignalSessionState.connect(this, &CallClient::OnSessionState); +} + +void CallClient::OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) { + if (state == cricket::Session::STATE_RECEIVEDINITIATE) { + buzz::Jid jid(session->remote_name()); + console_->Printf("Incoming call from '%s'", jid.Str().c_str()); + call_ = call; + session_ = session; + incoming_call_ = true; + } else if (state == cricket::Session::STATE_SENTINITIATE) { + console_->Print("calling..."); + } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) { + console_->Print("call answered"); + } else if (state == cricket::Session::STATE_RECEIVEDREJECT) { + console_->Print("call not answered"); + } else if (state == cricket::Session::STATE_INPROGRESS) { + console_->Print("call in progress"); + } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) { + console_->Print("other side hung up"); + } + } + +void CallClient::InitPresence() { + presence_push_ = new buzz::PresencePushTask(xmpp_client_); + presence_push_->SignalStatusUpdate.connect( + this, &CallClient::OnStatusUpdate); + presence_push_->Start(); + + buzz::Status my_status; + my_status.set_jid(xmpp_client_->jid()); + my_status.set_available(true); + my_status.set_show(buzz::Status::SHOW_ONLINE); + my_status.set_priority(0); + my_status.set_know_capabilities(true); + my_status.set_phone_capability(true); + my_status.set_is_google_client(true); + my_status.set_version("1.0.0.66"); + + buzz::PresenceOutTask* presence_out_ = + new buzz::PresenceOutTask(xmpp_client_); + presence_out_->Send(my_status); + presence_out_->Start(); +} + +void CallClient::OnStatusUpdate(const buzz::Status& status) { + RosterItem item; + item.jid = status.jid(); + item.show = status.show(); + item.status = status.status(); + + std::string key = item.jid.Str(); + + if (status.available() && status.phone_capability()) { + console_->Printf("Adding to roster: %s", key.c_str()); + (*roster_)[key] = item; + } else { + console_->Printf("Removing from roster: %s", key.c_str()); + RosterMap::iterator iter = roster_->find(key); + if (iter != roster_->end()) + roster_->erase(iter); + } +} + +void CallClient::PrintRoster() { + console_->SetPrompting(false); + console_->Printf("Roster contains %d callable", roster_->size()); + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + console_->Printf("%s - %s", + iter->second.jid.BareJid().Str().c_str(), + DescribeStatus(iter->second.show, iter->second.status)); + iter++; + } + console_->SetPrompting(true); +} + +void CallClient::MakeCallTo(const std::string& name) { + bool found = false; + buzz::Jid found_jid; + buzz::Jid callto_jid = buzz::Jid(name); + RosterMap::iterator iter = roster_->begin(); + while (iter != roster_->end()) { + if (iter->second.jid.BareEquals(callto_jid)) { + found = true; + found_jid = iter->second.jid; + break; + } + ++iter; + } + + + if (found) { + console_->Printf("Found online friend '%s'", found_jid.Str().c_str()); + phone_client()->SignalCallDestroy.connect( + this, &CallClient::OnCallDestroy); + if (!call_) { + call_ = phone_client()->CreateCall(); + console_->SetPrompt(found_jid.Str().c_str()); + call_->SignalSessionState.connect(this, &CallClient::OnSessionState); + session_ = call_->InitiateSession(found_jid, NULL); + } + phone_client()->SetFocus(call_); + } else { + console_->Printf("Could not find online friend '%s'", name.c_str()); + } +} diff --git a/Plugins/jingle/libjingle/talk/examples/call/callclient.h b/Plugins/jingle/libjingle/talk/examples/call/callclient.h new file mode 100644 index 0000000..1138f52 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/callclient.h @@ -0,0 +1,105 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ +#define CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ + +#include <map> +#include <string> +#include "talk/base/autodetectproxy.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/client/httpportallocator.h" +#include "talk/xmpp/xmppclient.h" +#include "talk/examples/login/status.h" +#include "talk/examples/call/console.h" + +namespace buzz { +class PresencePushTask; +class Status; +} + +namespace talk_base { +class Thread; +class NetworkManager; +} + +namespace cricket { +class PortAllocator; +class PhoneSessionClient; +class Receiver; +class Call; +class SessionManagerTask; +} + +struct RosterItem { + buzz::Jid jid; + buzz::Status::Show show; + std::string status; +}; + +class CallClient: public sigslot::has_slots<> { +public: + CallClient(buzz::XmppClient* xmpp_client); + ~CallClient(); + + cricket::PhoneSessionClient* phone_client() const { return phone_client_; } + + void PrintRoster(); + void MakeCallTo(const std::string& name); + void SetConsole(Console *console) {console_ = console;} + void ParseLine(const std::string &str); + +private: + typedef std::map<std::string,RosterItem> RosterMap; + + Console *console_; + buzz::XmppClient* xmpp_client_; + talk_base::Thread* worker_thread_; + talk_base::NetworkManager network_manager_; + talk_base::AutoDetectProxy *proxy_detect_; + cricket::HttpPortAllocator* port_allocator_; + cricket::SessionManager* session_manager_; + cricket::SessionManagerTask* session_manager_task_; + cricket::PhoneSessionClient* phone_client_; + + cricket::Call* call_; + cricket::Session *session_; + bool incoming_call_; + + buzz::PresencePushTask* presence_push_; + RosterMap* roster_; + + void OnStateChange(buzz::XmppEngine::State state); + void OnJingleInfo(const std::string &relay_token, const std::vector<std::string> &relay_hosts, + const std::vector<talk_base::SocketAddress> &stun_hosts); + void OnProxyDetect(talk_base::SignalThread *thread); + void InitPhone(); + void OnRequestSignaling(); + void OnCallCreate(cricket::Call* call); + void OnCallDestroy(cricket::Call* call); + const std::string strerror(buzz::XmppEngine::Error err); + void OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state); + + void InitPresence(); + void OnStatusUpdate(const buzz::Status& status); +}; + +#endif // CRICKET_EXAMPLES_CALL_CALLCLIENT_H__ diff --git a/Plugins/jingle/libjingle/talk/examples/call/console.cc b/Plugins/jingle/libjingle/talk/examples/call/console.cc new file mode 100644 index 0000000..d489fd8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/console.cc @@ -0,0 +1,77 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _CRT_SECURE_NO_DEPRECATE 1 + +#include <cassert> +#include "talk/base/messagequeue.h" +#include "talk/base/stringutils.h" +#include "talk/examples/call/console.h" +#include "talk/examples/call/callclient.h" + +Console::Console(talk_base::Thread *thread, CallClient *client) : + client_thread_(thread), client_(client), prompt_(std::string("call")), + prompting_(true) { +} + +void Console::StartConsole() { + char input_buffer[64]; + for (;;) { + fgets(input_buffer, sizeof(input_buffer), stdin); + client_thread_->Post(this, MSG_INPUT, + new talk_base::TypedMessageData<std::string>(input_buffer)); + } +} + +void Console::OnMessage(talk_base::Message *msg) { + switch (msg->message_id) { + case MSG_START: + StartConsole(); + break; + case MSG_INPUT: + talk_base::TypedMessageData<std::string> *data = + static_cast<talk_base::TypedMessageData<std::string>*>(msg->pdata); + client_->ParseLine(data->data()); + break; + } +} + +void Console::Print(const char* str) { + printf("\n%s", str); + if (prompting_) + printf("\n(%s) ", prompt_.c_str()); +} + +void Console::Print(const std::string& str) { + Print(str.c_str()); +} + +void Console::Printf(const char* format, ...) { + va_list ap; + va_start(ap, format); + + char buf[4096]; + int size = vsnprintf(buf, sizeof(buf), format, ap); + assert(size >= 0); + assert(size < static_cast<int>(sizeof(buf))); + buf[size] = '\0'; + Print(buf); + + va_end(ap); +}
\ No newline at end of file diff --git a/Plugins/jingle/libjingle/talk/examples/call/console.h b/Plugins/jingle/libjingle/talk/examples/call/console.h new file mode 100644 index 0000000..264251a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/call/console.h @@ -0,0 +1,60 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CRICKET_EXAMPLES_CALL_CONSOLE_H__ +#define CRICKET_EXAMPLES_CALL_CONSOLE_H__ + +#include "talk/base/thread.h" +#include "talk/base/messagequeue.h" + +class CallClient; + +enum { + MSG_START, + MSG_INPUT, +}; + +class Console : public talk_base::MessageHandler { + public: + Console(talk_base::Thread *thread, CallClient *client); + virtual void OnMessage(talk_base::Message *msg); + void SetPrompt(const char *prompt) { + prompt_ = prompt ? std::string(prompt) : std::string("call"); + } + void SetPrompting(bool prompting) { + prompting_ = prompting; + if (prompting) + printf("\n(%s) ", prompt_.c_str()); + } + bool prompting() {return prompting_;} + + void Print(const char* str); + void Print(const std::string& str); + void Printf(const char* format, ...); + private: + CallClient *client_; + talk_base::Thread *client_thread_; + void StartConsole(); + void ParseLine(std::string &str); + std::string prompt_; + bool prompting_; +}; + +#endif // CRICKET_EXAMPLES_CALL_CONSOLE_H__ + diff --git a/Plugins/jingle/libjingle/talk/examples/login/Makefile.am b/Plugins/jingle/libjingle/talk/examples/login/Makefile.am new file mode 100644 index 0000000..7bb7804 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES= libcricketexampleslogin.la +libcricketexampleslogin_la_SOURCES = xmppsocket.cc \ + xmppauth.cc \ + xmppthread.cc \ + xmpppump.cc \ + jingleinfotask.cc \ + presenceouttask.cc \ + presencepushtask.cc + +noinst_HEADERS = xmppauth.h \ + xmpppump.h \ + xmppsocket.h \ + xmppthread.h \ + jingleinfotask.h \ + presenceouttask.h \ + presencepushtask.h \ + status.h + +bin_PROGRAMS = login +login_CXXFLAGS = $(AM_CXXFLAGS) +login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc +login_LDADD = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) -lpthread -lssl -lcrypto +AM_CPPFLAGS = -DPOSIX diff --git a/Plugins/jingle/libjingle/talk/examples/login/Makefile.in b/Plugins/jingle/libjingle/talk/examples/login/Makefile.in new file mode 100644 index 0000000..3d70be5 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/Makefile.in @@ -0,0 +1,600 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = login$(EXEEXT) +subdir = talk/examples/login +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcricketexampleslogin_la_LIBADD = +am_libcricketexampleslogin_la_OBJECTS = xmppsocket.lo xmppauth.lo \ + xmppthread.lo xmpppump.lo jingleinfotask.lo presenceouttask.lo \ + presencepushtask.lo +libcricketexampleslogin_la_OBJECTS = \ + $(am_libcricketexampleslogin_la_OBJECTS) +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_login_OBJECTS = login-login_main.$(OBJEXT) \ + login-xmppsocket.$(OBJEXT) login-xmppthread.$(OBJEXT) \ + login-xmpppump.$(OBJEXT) login-xmppauth.$(OBJEXT) +login_OBJECTS = $(am_login_OBJECTS) +am__DEPENDENCIES_1 = +login_DEPENDENCIES = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libcricketexampleslogin_la_SOURCES) $(login_SOURCES) +DIST_SOURCES = $(libcricketexampleslogin_la_SOURCES) $(login_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +noinst_LTLIBRARIES = libcricketexampleslogin.la +libcricketexampleslogin_la_SOURCES = xmppsocket.cc \ + xmppauth.cc \ + xmppthread.cc \ + xmpppump.cc \ + jingleinfotask.cc \ + presenceouttask.cc \ + presencepushtask.cc + +noinst_HEADERS = xmppauth.h \ + xmpppump.h \ + xmppsocket.h \ + xmppthread.h \ + jingleinfotask.h \ + presenceouttask.h \ + presencepushtask.h \ + status.h + +login_CXXFLAGS = $(AM_CXXFLAGS) +login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc +login_LDADD = $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) -lpthread -lssl -lcrypto + +AM_CPPFLAGS = -DPOSIX +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/login/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/examples/login/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcricketexampleslogin.la: $(libcricketexampleslogin_la_OBJECTS) $(libcricketexampleslogin_la_DEPENDENCIES) + $(CXXLINK) $(libcricketexampleslogin_la_LDFLAGS) $(libcricketexampleslogin_la_OBJECTS) $(libcricketexampleslogin_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +login$(EXEEXT): $(login_OBJECTS) $(login_DEPENDENCIES) + @rm -f login$(EXEEXT) + $(CXXLINK) $(login_LDFLAGS) $(login_OBJECTS) $(login_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jingleinfotask.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-login_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppauth.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmpppump.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppsocket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login-xmppthread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presenceouttask.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presencepushtask.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppauth.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmpppump.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppsocket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmppthread.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +login-login_main.o: login_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-login_main.o -MD -MP -MF "$(DEPDIR)/login-login_main.Tpo" -c -o login-login_main.o `test -f 'login_main.cc' || echo '$(srcdir)/'`login_main.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-login_main.Tpo" "$(DEPDIR)/login-login_main.Po"; else rm -f "$(DEPDIR)/login-login_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='login_main.cc' object='login-login_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-login_main.o `test -f 'login_main.cc' || echo '$(srcdir)/'`login_main.cc + +login-login_main.obj: login_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-login_main.obj -MD -MP -MF "$(DEPDIR)/login-login_main.Tpo" -c -o login-login_main.obj `if test -f 'login_main.cc'; then $(CYGPATH_W) 'login_main.cc'; else $(CYGPATH_W) '$(srcdir)/login_main.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-login_main.Tpo" "$(DEPDIR)/login-login_main.Po"; else rm -f "$(DEPDIR)/login-login_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='login_main.cc' object='login-login_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-login_main.obj `if test -f 'login_main.cc'; then $(CYGPATH_W) 'login_main.cc'; else $(CYGPATH_W) '$(srcdir)/login_main.cc'; fi` + +login-xmppsocket.o: xmppsocket.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppsocket.o -MD -MP -MF "$(DEPDIR)/login-xmppsocket.Tpo" -c -o login-xmppsocket.o `test -f 'xmppsocket.cc' || echo '$(srcdir)/'`xmppsocket.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppsocket.Tpo" "$(DEPDIR)/login-xmppsocket.Po"; else rm -f "$(DEPDIR)/login-xmppsocket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppsocket.cc' object='login-xmppsocket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppsocket.o `test -f 'xmppsocket.cc' || echo '$(srcdir)/'`xmppsocket.cc + +login-xmppsocket.obj: xmppsocket.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppsocket.obj -MD -MP -MF "$(DEPDIR)/login-xmppsocket.Tpo" -c -o login-xmppsocket.obj `if test -f 'xmppsocket.cc'; then $(CYGPATH_W) 'xmppsocket.cc'; else $(CYGPATH_W) '$(srcdir)/xmppsocket.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppsocket.Tpo" "$(DEPDIR)/login-xmppsocket.Po"; else rm -f "$(DEPDIR)/login-xmppsocket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppsocket.cc' object='login-xmppsocket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppsocket.obj `if test -f 'xmppsocket.cc'; then $(CYGPATH_W) 'xmppsocket.cc'; else $(CYGPATH_W) '$(srcdir)/xmppsocket.cc'; fi` + +login-xmppthread.o: xmppthread.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppthread.o -MD -MP -MF "$(DEPDIR)/login-xmppthread.Tpo" -c -o login-xmppthread.o `test -f 'xmppthread.cc' || echo '$(srcdir)/'`xmppthread.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppthread.Tpo" "$(DEPDIR)/login-xmppthread.Po"; else rm -f "$(DEPDIR)/login-xmppthread.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppthread.cc' object='login-xmppthread.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppthread.o `test -f 'xmppthread.cc' || echo '$(srcdir)/'`xmppthread.cc + +login-xmppthread.obj: xmppthread.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppthread.obj -MD -MP -MF "$(DEPDIR)/login-xmppthread.Tpo" -c -o login-xmppthread.obj `if test -f 'xmppthread.cc'; then $(CYGPATH_W) 'xmppthread.cc'; else $(CYGPATH_W) '$(srcdir)/xmppthread.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppthread.Tpo" "$(DEPDIR)/login-xmppthread.Po"; else rm -f "$(DEPDIR)/login-xmppthread.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppthread.cc' object='login-xmppthread.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppthread.obj `if test -f 'xmppthread.cc'; then $(CYGPATH_W) 'xmppthread.cc'; else $(CYGPATH_W) '$(srcdir)/xmppthread.cc'; fi` + +login-xmpppump.o: xmpppump.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmpppump.o -MD -MP -MF "$(DEPDIR)/login-xmpppump.Tpo" -c -o login-xmpppump.o `test -f 'xmpppump.cc' || echo '$(srcdir)/'`xmpppump.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmpppump.Tpo" "$(DEPDIR)/login-xmpppump.Po"; else rm -f "$(DEPDIR)/login-xmpppump.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmpppump.cc' object='login-xmpppump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmpppump.o `test -f 'xmpppump.cc' || echo '$(srcdir)/'`xmpppump.cc + +login-xmpppump.obj: xmpppump.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmpppump.obj -MD -MP -MF "$(DEPDIR)/login-xmpppump.Tpo" -c -o login-xmpppump.obj `if test -f 'xmpppump.cc'; then $(CYGPATH_W) 'xmpppump.cc'; else $(CYGPATH_W) '$(srcdir)/xmpppump.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmpppump.Tpo" "$(DEPDIR)/login-xmpppump.Po"; else rm -f "$(DEPDIR)/login-xmpppump.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmpppump.cc' object='login-xmpppump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmpppump.obj `if test -f 'xmpppump.cc'; then $(CYGPATH_W) 'xmpppump.cc'; else $(CYGPATH_W) '$(srcdir)/xmpppump.cc'; fi` + +login-xmppauth.o: xmppauth.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppauth.o -MD -MP -MF "$(DEPDIR)/login-xmppauth.Tpo" -c -o login-xmppauth.o `test -f 'xmppauth.cc' || echo '$(srcdir)/'`xmppauth.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppauth.Tpo" "$(DEPDIR)/login-xmppauth.Po"; else rm -f "$(DEPDIR)/login-xmppauth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppauth.cc' object='login-xmppauth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppauth.o `test -f 'xmppauth.cc' || echo '$(srcdir)/'`xmppauth.cc + +login-xmppauth.obj: xmppauth.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -MT login-xmppauth.obj -MD -MP -MF "$(DEPDIR)/login-xmppauth.Tpo" -c -o login-xmppauth.obj `if test -f 'xmppauth.cc'; then $(CYGPATH_W) 'xmppauth.cc'; else $(CYGPATH_W) '$(srcdir)/xmppauth.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/login-xmppauth.Tpo" "$(DEPDIR)/login-xmppauth.Po"; else rm -f "$(DEPDIR)/login-xmppauth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='xmppauth.cc' object='login-xmppauth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(login_CXXFLAGS) $(CXXFLAGS) -c -o login-xmppauth.obj `if test -f 'xmppauth.cc'; then $(CYGPATH_W) 'xmppauth.cc'; else $(CYGPATH_W) '$(srcdir)/xmppauth.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool clean-noinstLTLIBRARIES ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc new file mode 100644 index 0000000..4f75853 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.cc @@ -0,0 +1,112 @@ +#include "talk/examples/login/jingleinfotask.h" +#include "talk/base/socketaddress.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppclient.h" + + +namespace buzz { + +class JingleInfoTask::JingleInfoGetTask : public XmppTask { +public: + JingleInfoGetTask(Task * parent) : XmppTask(parent, XmppEngine::HL_SINGLE), + done_(false) {} + + virtual int ProcessStart() { + scoped_ptr<XmlElement> get(MakeIq(STR_GET, JID_EMPTY, task_id())); + get->AddElement(new XmlElement(QN_JINGLE_INFO_QUERY, true)); + if (SendStanza(get.get()) != XMPP_RETURN_OK) { + return STATE_ERROR; + } + return STATE_RESPONSE; + } + virtual int ProcessResponse() { + if (done_) + return STATE_DONE; + return STATE_BLOCKED; + } + +protected: + virtual bool HandleStanza(const XmlElement * stanza) { + if (!MatchResponseIq(stanza, JID_EMPTY, task_id())) + return false; + + if (stanza->Attr(QN_TYPE) != STR_RESULT) + return false; + + // Queue the stanza with the parent so these don't get handled out of order + JingleInfoTask* parent = static_cast<JingleInfoTask*>(GetParent()); + parent->QueueStanza(stanza); + + // Wake ourselves so we can go into the done state + done_ = true; + Wake(); + return true; + } + + bool done_; +}; + + +void JingleInfoTask::RefreshJingleInfoNow() { + JingleInfoGetTask* get_task = new JingleInfoGetTask(this); + get_task->Start(); +} + +bool +JingleInfoTask::HandleStanza(const XmlElement * stanza) { + if (!MatchRequestIq(stanza, "set", QN_JINGLE_INFO_QUERY)) + return false; + + // only respect relay push from the server + Jid from(stanza->Attr(QN_FROM)); + if (from != JID_EMPTY && + !from.BareEquals(GetClient()->jid()) && + from != Jid(GetClient()->jid().domain())) + return false; + + QueueStanza(stanza); + return true; +} + +int +JingleInfoTask::ProcessStart() { + std::vector<std::string> relay_hosts; + std::vector<talk_base::SocketAddress> stun_hosts; + std::string relay_token; + const XmlElement * stanza = NextStanza(); + if (stanza == NULL) + return STATE_BLOCKED; + const XmlElement * query = stanza->FirstNamed(QN_JINGLE_INFO_QUERY); + if (query == NULL) + return STATE_START; + const XmlElement *stun = query->FirstNamed(QN_JINGLE_INFO_STUN); + if (stun) { + for (const XmlElement *server = stun->FirstNamed(QN_JINGLE_INFO_SERVER); + server != NULL; server = server->NextNamed(QN_JINGLE_INFO_SERVER)) { + std::string host = server->Attr(QN_JINGLE_INFO_HOST); + std::string port = server->Attr(QN_JINGLE_INFO_UDP); + if (host != STR_EMPTY && host != STR_EMPTY) + stun_hosts.push_back(talk_base::SocketAddress(host, atoi(port.c_str()))); + } + } + + const XmlElement *relay = query->FirstNamed(QN_JINGLE_INFO_RELAY); + if (relay) { + relay_token = relay->TextNamed(QN_JINGLE_INFO_TOKEN); + for (const XmlElement *server = relay->FirstNamed(QN_JINGLE_INFO_SERVER); + server != NULL; server = server->NextNamed(QN_JINGLE_INFO_SERVER)) { + std::string host = server->Attr(QN_JINGLE_INFO_HOST); + if (host != STR_EMPTY) { + relay_hosts.push_back(host); + } + } + } + SignalJingleInfo(relay_token, relay_hosts, stun_hosts); + return STATE_START; +} + + +} + + + diff --git a/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h new file mode 100644 index 0000000..be7073b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/jingleinfotask.h @@ -0,0 +1,36 @@ +#ifndef _JINGLEINFOTASK_H_ +#define _JINGLEINFOTASK_H_ + +#include "talk/p2p/client/httpportallocator.h" +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/base/sigslot.h" +#include "status.h" + +namespace buzz { + +class JingleInfoTask : public XmppTask { + + public: + JingleInfoTask(Task * parent) : + XmppTask(parent, XmppEngine::HL_TYPE) {} + + virtual int ProcessStart(); + void RefreshJingleInfoNow(); + + sigslot::signal3<const std::string &, + const std::vector<std::string> &, + const std::vector<talk_base::SocketAddress> &> SignalJingleInfo; + + protected: + class JingleInfoGetTask; + friend class JingleInfoGetTask; + + virtual bool HandleStanza(const XmlElement * stanza); +}; + + +} + +#endif + diff --git a/Plugins/jingle/libjingle/talk/examples/login/login_main.cc b/Plugins/jingle/libjingle/talk/examples/login/login_main.cc new file mode 100644 index 0000000..186d6f7 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/login_main.cc @@ -0,0 +1,63 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/thread.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include <iostream> + +int main(int argc, char **argv) { + printf("Auth Cookie: "); + fflush(stdout); + + char auth_cookie[256]; + scanf("%s", auth_cookie); + + char username[256]; + scanf("%s", username); + + // Start xmpp on a different thread + XmppThread thread; + thread.Start(); + + buzz::XmppClientSettings xcs; + xcs.set_user(username); + xcs.set_host("gmail.com"); + xcs.set_use_tls(false); + xcs.set_auth_cookie(auth_cookie); + xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222)); + thread.Login(xcs); + + // Use main thread for console input + std::string line; + while (std::getline(std::cin, line)) { + if (line == "quit") + break; + } + return 0; +} + diff --git a/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc new file mode 100644 index 0000000..1eaf516 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.cc @@ -0,0 +1,133 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <time.h> +#include <sstream> +#include "talk/base/stringencode.h" +#include "talk/examples/login/presenceouttask.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/xmppclient.h" + +namespace buzz { + +XmppReturnStatus +PresenceOutTask::Send(const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + stanza_.reset(TranslateStatus(s)); + return XMPP_RETURN_OK; +} + +XmppReturnStatus +PresenceOutTask::SendDirected(const Jid & j, const Status & s) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = TranslateStatus(s); + presence->AddAttr(QN_TO, j.Str()); + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) { + if (GetState() != STATE_INIT) + return XMPP_RETURN_BADSTATE; + + XmlElement * presence = new XmlElement(QN_PRESENCE); + presence->AddAttr(QN_TO, jid.Str()); + presence->AddAttr(QN_TYPE, "probe"); + + stanza_.reset(presence); + return XMPP_RETURN_OK; +} + +int +PresenceOutTask::ProcessStart() { + if (SendStanza(stanza_.get()) != XMPP_RETURN_OK) + return STATE_ERROR; + return STATE_DONE; +} + +XmlElement * +PresenceOutTask::TranslateStatus(const Status & s) { + XmlElement * result = new XmlElement(QN_PRESENCE); + if (!s.available()) { + result->AddAttr(QN_TYPE, STR_UNAVAILABLE); + } + else { + if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) { + result->AddElement(new XmlElement(QN_SHOW)); + switch (s.show()) { + default: + result->AddText(STR_SHOW_AWAY, 1); + break; + case Status::SHOW_XA: + result->AddText(STR_SHOW_XA, 1); + break; + case Status::SHOW_DND: + result->AddText(STR_SHOW_DND, 1); + break; + case Status::SHOW_CHAT: + result->AddText(STR_SHOW_CHAT, 1); + break; + } + } + + result->AddElement(new XmlElement(QN_STATUS)); + result->AddText(s.status(), 1); + + std::string pri; + talk_base::ToString(s.priority(), &pri); + + result->AddElement(new XmlElement(QN_PRIORITY)); + result->AddText(pri, 1); + + if (s.know_capabilities() && s.is_google_client()) { + std::string caps; + if (s.fileshare_capability()) + caps += "share-v1"; + if (s.phone_capability()) + caps += " voice-v1"; + result->AddElement(new XmlElement(QN_CAPS_C, true)); + result->AddAttr(QN_NODE, GOOGLE_CLIENT_NODE, 1); + result->AddAttr(QN_VER, s.version(), 1); + result->AddAttr(QN_EXT, caps, 1); + } + + // Put the delay mark on the presence according to JEP-0091 + { + result->AddElement(new XmlElement(kQnDelayX, true)); + + // This here is why we *love* the C runtime + time_t current_time_seconds; + time(¤t_time_seconds); + struct tm* current_time = gmtime(¤t_time_seconds); + char output[256]; + strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time); + result->AddAttr(kQnStamp, output, 1); + } + + } + + return result; +} + + +} diff --git a/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h new file mode 100644 index 0000000..bd691f4 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/presenceouttask.h @@ -0,0 +1,46 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PRESENCEOUTTASK_H_ +#define _PRESENCEOUTTASK_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/examples/login/status.h" + +namespace buzz { + +class PresenceOutTask : public XmppTask { +public: + PresenceOutTask(Task * parent) : XmppTask(parent) {} + virtual ~PresenceOutTask() {} + + XmppReturnStatus Send(const Status & s); + XmppReturnStatus SendDirected(const Jid & j, const Status & s); + XmppReturnStatus SendProbe(const Jid& jid); + + virtual int ProcessStart(); +private: + XmlElement * TranslateStatus(const Status & s); + scoped_ptr<XmlElement> stanza_; +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc new file mode 100644 index 0000000..4895190 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.cc @@ -0,0 +1,161 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "talk/base/stringencode.h" +#include "talk/examples/login/presencepushtask.h" +#include "talk/xmpp/constants.h" +#include <sstream> + + +namespace buzz { + +// string helper functions ----------------------------------------------------- + +static bool +IsXmlSpace(int ch) { + return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; +} + +static bool +ListContainsToken(const std::string & list, const std::string & token) { + size_t i = list.find(token); + if (i == std::string::npos || token.empty()) + return false; + bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); + bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()])); + return boundary_before && boundary_after; +} + + +bool +PresencePushTask::HandleStanza(const XmlElement * stanza) { + if (stanza->Name() != QN_PRESENCE) + return false; + if (stanza->HasAttr(QN_TYPE) && stanza->Attr(QN_TYPE) != STR_UNAVAILABLE) + return false; + QueueStanza(stanza); + return true; +} + +static bool IsUtf8FirstByte(int c) { + return (((c)&0x80)==0) || // is single byte + ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte +} + +int +PresencePushTask::ProcessStart() { + const XmlElement * stanza = NextStanza(); + if (stanza == NULL) + return STATE_BLOCKED; + Status s; + + s.set_jid(Jid(stanza->Attr(QN_FROM))); + + if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) { + s.set_available(false); + SignalStatusUpdate(s); + } + else { + s.set_available(true); + const XmlElement * status = stanza->FirstNamed(QN_STATUS); + if (status != NULL) { + s.set_status(status->BodyText()); + + // Truncate status messages longer than 300 bytes + if (s.status().length() > 300) { + size_t len = 300; + + // Be careful not to split legal utf-8 chars in half + while (!IsUtf8FirstByte(s.status()[len]) && len > 0) { + len -= 1; + } + std::string truncated(s.status(), 0, len); + s.set_status(truncated); + } + } + + const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY); + if (priority != NULL) { + int pri; + if (talk_base::FromString(priority->BodyText(), &pri)) { + s.set_priority(pri); + } + } + + const XmlElement * show = stanza->FirstNamed(QN_SHOW); + if (show == NULL || show->FirstChild() == NULL) { + s.set_show(Status::SHOW_ONLINE); + } + else { + if (show->BodyText() == "away") { + s.set_show(Status::SHOW_AWAY); + } + else if (show->BodyText() == "xa") { + s.set_show(Status::SHOW_XA); + } + else if (show->BodyText() == "dnd") { + s.set_show(Status::SHOW_DND); + } + else if (show->BodyText() == "chat") { + s.set_show(Status::SHOW_CHAT); + } + else { + s.set_show(Status::SHOW_ONLINE); + } + } + + const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C); + if (caps != NULL) { + std::string node = caps->Attr(QN_NODE); + std::string ver = caps->Attr(QN_VER); + std::string exts = caps->Attr(QN_EXT); + + s.set_know_capabilities(true); + + if (node == GOOGLE_CLIENT_NODE) { + s.set_is_google_client(true); + s.set_version(ver); + if (ListContainsToken(exts, "voice-v1")) { + s.set_phone_capability(true); + } + if (ListContainsToken(exts, "share-v1")) { + s.set_fileshare_capability(true); + } + } + } + + const XmlElement* delay = stanza->FirstNamed(kQnDelayX); + if (delay != NULL) { + // Ideally we would parse this according to the Psuedo ISO-8601 rules + // that are laid out in JEP-0082: + // http://www.jabber.org/jeps/jep-0082.html + std::string stamp = delay->Attr(kQnStamp); + s.set_sent_time(stamp); + } + + SignalStatusUpdate(s); + } + + return STATE_START; +} + + +} + + diff --git a/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h new file mode 100644 index 0000000..23cd1b8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/presencepushtask.h @@ -0,0 +1,44 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PRESENCEPUSHTASK_H_ +#define _PRESENCEPUSHTASK_H_ + +#include "talk/xmpp/xmppengine.h" +#include "talk/xmpp/xmpptask.h" +#include "talk/base/sigslot.h" +#include "talk/examples/login/status.h" + +namespace buzz { + +class PresencePushTask : public XmppTask { + +public: + PresencePushTask(Task * parent) : XmppTask(parent, XmppEngine::HL_TYPE) {} + virtual int ProcessStart(); + sigslot::signal1<const Status &>SignalStatusUpdate; + +protected: + virtual bool HandleStanza(const XmlElement * stanza); +}; + + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/examples/login/status.h b/Plugins/jingle/libjingle/talk/examples/login/status.h new file mode 100644 index 0000000..69f82b1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/login/status.h @@ -0,0 +1,212 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" + +#define GOOGLE_CLIENT_NODE "http://www.google.com/xmpp/client/caps" + +namespace buzz { + +class Status { +public: + Status() : + pri_(0), + show_(SHOW_NONE), + available_(false), + e_code_(0), + phone_capability_(false), + fileshare_capability_(false), + know_capabilities_(false), + is_google_client_(false), + feedback_probation_(false) {}; + + ~Status() {} + + // These are arranged in "priority order", i.e., if we see + // two statuses at the same priority but with different Shows, + // we will show the one with the highest show in the following + // order. + enum Show { + SHOW_NONE = 0, + SHOW_OFFLINE = 1, + SHOW_XA = 2, + SHOW_AWAY = 3, + SHOW_DND = 4, + SHOW_ONLINE = 5, + SHOW_CHAT = 6, + }; + + const Jid & jid() const { return jid_; } + int priority() const { return pri_; } + Show show() const { return show_; } + const std::string & status() const { return status_; } + bool available() const { return available_ ; } + int error_code() const { return e_code_; } + const std::string & error_string() const { return e_str_; } + bool know_capabilities() const { return know_capabilities_; } + bool phone_capability() const { return phone_capability_; } + bool fileshare_capability() const { return fileshare_capability_; } + bool is_google_client() const { return is_google_client_; } + const std::string & version() const { return version_; } + bool feedback_probation() const { return feedback_probation_; } + const std::string& sent_time() const { return sent_time_; } + + void set_jid(const Jid & jid) { jid_ = jid; } + void set_priority(int pri) { pri_ = pri; } + void set_show(Show show) { show_ = show; } + void set_status(const std::string & status) { status_ = status; } + void set_available(bool a) { available_ = a; } + void set_error(int e_code, const std::string e_str) + { e_code_ = e_code; e_str_ = e_str; } + void set_know_capabilities(bool f) { know_capabilities_ = f; } + void set_phone_capability(bool f) { phone_capability_ = f; } + void set_fileshare_capability(bool f) { fileshare_capability_ = f; } + void set_is_google_client(bool f) { is_google_client_ = f; } + void set_version(const std::string & v) { version_ = v; } + void set_feedback_probation(bool f) { feedback_probation_ = f; } + void set_sent_time(const std::string& time) { sent_time_ = time; } + + void UpdateWith(const Status & new_value) { + if (!new_value.know_capabilities()) { + bool k = know_capabilities(); + bool i = is_google_client(); + bool p = phone_capability(); + std::string v = version(); + + *this = new_value; + + set_know_capabilities(k); + set_is_google_client(i); + set_phone_capability(p); + set_version(v); + } + else { + *this = new_value; + } + } + + bool HasQuietStatus() const { + if (status_.empty()) + return false; + return !(QuietStatus().empty()); + } + + // Knowledge of other clients' silly automatic status strings - + // Don't show these. + std::string QuietStatus() const { + if (jid_.resource().find("Psi") != std::string::npos) { + if (status_ == "Online" || + status_.find("Auto Status") != std::string::npos) + return STR_EMPTY; + } + if (jid_.resource().find("Gaim") != std::string::npos) { + if (status_ == "Sorry, I ran out for a bit!") + return STR_EMPTY; + } + return TrimStatus(status_); + } + + std::string ExplicitStatus() const { + std::string result = QuietStatus(); + if (result.empty()) { + result = ShowStatus(); + } + return result; + } + + std::string ShowStatus() const { + std::string result; + if (!available()) { + result = "Offline"; + } + else { + switch (show()) { + case SHOW_AWAY: + case SHOW_XA: + result = "Idle"; + break; + case SHOW_DND: + result = "Busy"; + break; + case SHOW_CHAT: + result = "Chatty"; + break; + default: + result = "Available"; + break; + } + } + return result; + } + + static std::string TrimStatus(const std::string & st) { + std::string s(st); + int j = 0; + bool collapsing = true; + for (unsigned int i = 0; i < s.length(); i+= 1) { + if (s[i] <= ' ' && s[i] >= 0) { + if (collapsing) { + continue; + } + else { + s[j] = ' '; + j += 1; + collapsing = true; + } + } + else { + s[j] = s[i]; + j += 1; + collapsing = false; + } + } + if (collapsing && j > 0) { + j -= 1; + } + s.erase(j, s.length()); + return s; + } + +private: + Jid jid_; + int pri_; + Show show_; + std::string status_; + bool available_; + int e_code_; + std::string e_str_; + bool feedback_probation_; + + // capabilities (valid only if know_capabilities_ + bool know_capabilities_; + bool phone_capability_; + bool fileshare_capability_; + bool is_google_client_; + std::string version_; + + std::string sent_time_; // from the jabber:x:delay element +}; + +} + + +#endif diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am new file mode 100644 index 0000000..de779cd --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.am @@ -0,0 +1,15 @@ +bin_PROGRAMS = pcp +pcp_CXXFLAGS = $(AM_CXXFLAGS) +pcp_SOURCES = pcp_main.cc +pcp_LDADD = $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \ + $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) -lpthread -lssl -lcrypto + +AM_CPPFLAGS = -DPOSIX +EXTRA_DIST = pcp.vcproj diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in new file mode 100644 index 0000000..0bba040 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/pcp/Makefile.in @@ -0,0 +1,501 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = pcp$(EXEEXT) +subdir = talk/examples/pcp +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_pcp_OBJECTS = pcp-pcp_main.$(OBJEXT) +pcp_OBJECTS = $(am_pcp_OBJECTS) +am__DEPENDENCIES_1 = +pcp_DEPENDENCIES = \ + $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \ + $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(pcp_SOURCES) +DIST_SOURCES = $(pcp_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +pcp_CXXFLAGS = $(AM_CXXFLAGS) +pcp_SOURCES = pcp_main.cc +pcp_LDADD = $(top_srcdir)/talk/examples/login/libcricketexampleslogin.la \ + $(top_srcdir)/talk/session/fileshare/libcricketsessionfileshare.la \ + $(top_srcdir)/talk/session/tunnel/libcricketsessiontunnel.la \ + $(top_srcdir)/talk/p2p/client/libcricketp2pclient.la \ + $(top_srcdir)/talk/p2p/base/libcricketp2pbase.la \ + $(top_srcdir)/talk/xmpp/libcricketxmpp.la \ + $(top_srcdir)/talk/xmllite/libcricketxmllite.la \ + $(top_srcdir)/talk/base/libcricketbase.la \ + $(EXPAT_LIBS) -lpthread -lssl -lcrypto + +AM_CPPFLAGS = -DPOSIX +EXTRA_DIST = pcp.vcproj +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/examples/pcp/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/examples/pcp/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +pcp$(EXEEXT): $(pcp_OBJECTS) $(pcp_DEPENDENCIES) + @rm -f pcp$(EXEEXT) + $(CXXLINK) $(pcp_LDFLAGS) $(pcp_OBJECTS) $(pcp_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp-pcp_main.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +pcp-pcp_main.o: pcp_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -MT pcp-pcp_main.o -MD -MP -MF "$(DEPDIR)/pcp-pcp_main.Tpo" -c -o pcp-pcp_main.o `test -f 'pcp_main.cc' || echo '$(srcdir)/'`pcp_main.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/pcp-pcp_main.Tpo" "$(DEPDIR)/pcp-pcp_main.Po"; else rm -f "$(DEPDIR)/pcp-pcp_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pcp_main.cc' object='pcp-pcp_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -c -o pcp-pcp_main.o `test -f 'pcp_main.cc' || echo '$(srcdir)/'`pcp_main.cc + +pcp-pcp_main.obj: pcp_main.cc +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -MT pcp-pcp_main.obj -MD -MP -MF "$(DEPDIR)/pcp-pcp_main.Tpo" -c -o pcp-pcp_main.obj `if test -f 'pcp_main.cc'; then $(CYGPATH_W) 'pcp_main.cc'; else $(CYGPATH_W) '$(srcdir)/pcp_main.cc'; fi`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/pcp-pcp_main.Tpo" "$(DEPDIR)/pcp-pcp_main.Po"; else rm -f "$(DEPDIR)/pcp-pcp_main.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='pcp_main.cc' object='pcp-pcp_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcp_CXXFLAGS) $(CXXFLAGS) -c -o pcp-pcp_main.obj `if test -f 'pcp_main.cc'; then $(CYGPATH_W) 'pcp_main.cc'; else $(CYGPATH_W) '$(srcdir)/pcp_main.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj b/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj new file mode 100644 index 0000000..03c661a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/pcp/pcp.vcproj @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="pcp"
+ ProjectGUID="{EF5C9C17-874A-44C3-93CB-1165F03DBCD3}"
+ RootNamespace="pcp"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL"
+ MinimalRebuild="true"
+ ExceptionHandling="1"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib crypt32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\Desenvolvimento\libjingle-0.4.0\talk\third_party\Expat\StaticLibs"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../.."
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WINDOWS;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING"
+ ExceptionHandling="1"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comsupp.lib secur32.lib ws2_32.lib libexpatMT.lib Iphlpapi.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\login\jingleinfotask.cc"
+ >
+ </File>
+ <File
+ RelativePath="pcp_main.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presenceouttask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\presencepushtask.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppauth.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmpppump.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\login\xmppthread.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ 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>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc b/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc new file mode 100644 index 0000000..4d5646d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/examples/pcp/pcp_main.cc @@ -0,0 +1,615 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Tempe Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <iomanip> +#include <time.h> + +#ifndef WIN32 +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <iomanip> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#else +#include <direct.h> +//typedef _getcwd getcwd; +#include "talk/base/win32.h" +#endif + +#include "talk/base/fileutils.h" +#include "talk/base/pathutils.h" +#include "talk/base/helpers.h" +#include "talk/base/httpclient.h" +#include "talk/base/logging.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/ssladapter.h" +#include "talk/xmpp/xmppclientsettings.h" +#include "talk/examples/login/xmppthread.h" +#include "talk/examples/login/xmppauth.h" +#include "talk/p2p/client/httpportallocator.h" +#include "talk/p2p/client/sessionmanagertask.h" +#include "talk/session/fileshare/fileshare.h" +#include "talk/examples/login/presencepushtask.h" +#include "talk/examples/login/presenceouttask.h" +#include "talk/examples/login/jingleinfotask.h" + +#if defined(_MSC_VER) && (_MSC_VER < 1400) +// The following are necessary to properly link when compiling STL without +// /EHsc, otherwise known as C++ exceptions. +void __cdecl std::_Throw(const std::exception &) {} +std::_Prhand std::_Raise_handler = 0; +#endif + +void SetConsoleEcho(bool on) { +#ifdef WIN32 + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) + return; + + DWORD mode; + if (!GetConsoleMode(hIn, &mode)) + return; + + if (on) { + mode = mode | ENABLE_ECHO_INPUT; + } else { + mode = mode & ~ENABLE_ECHO_INPUT; + } + + SetConsoleMode(hIn, mode); +#else + if (on) + system("stty echo"); + else + system("stty -echo"); +#endif +} +class DebugLog : public sigslot::has_slots<> { +public: + DebugLog() : + debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0), + debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0), + censor_password_(false) + {} + char * debug_input_buf_; + int debug_input_len_; + int debug_input_alloc_; + char * debug_output_buf_; + int debug_output_len_; + int debug_output_alloc_; + bool censor_password_; + + void Input(const char * data, int len) { + if (debug_input_len_ + len > debug_input_alloc_) { + char * old_buf = debug_input_buf_; + debug_input_alloc_ = 4096; + while (debug_input_alloc_ < debug_input_len_ + len) { + debug_input_alloc_ *= 2; + } + debug_input_buf_ = new char[debug_input_alloc_]; + memcpy(debug_input_buf_, old_buf, debug_input_len_); + delete[] old_buf; + } + memcpy(debug_input_buf_ + debug_input_len_, data, len); + debug_input_len_ += len; + DebugPrint(debug_input_buf_, &debug_input_len_, false); + } + + void Output(const char * data, int len) { + if (debug_output_len_ + len > debug_output_alloc_) { + char * old_buf = debug_output_buf_; + debug_output_alloc_ = 4096; + while (debug_output_alloc_ < debug_output_len_ + len) { + debug_output_alloc_ *= 2; + } + debug_output_buf_ = new char[debug_output_alloc_]; + memcpy(debug_output_buf_, old_buf, debug_output_len_); + delete[] old_buf; + } + memcpy(debug_output_buf_ + debug_output_len_, data, len); + debug_output_len_ += len; + DebugPrint(debug_output_buf_, &debug_output_len_, true); + } + + static bool + IsAuthTag(const char * str, size_t len) { + if (str[0] == '<' && str[1] == 'a' && + str[2] == 'u' && + str[3] == 't' && + str[4] == 'h' && + str[5] <= ' ') { + std::string tag(str, len); + + if (tag.find("mechanism") != std::string::npos) + return true; + + } + return false; + } + + void + DebugPrint(char * buf, int * plen, bool output) { + int len = *plen; + if (len > 0) { + time_t tim = time(NULL); + struct tm * now = localtime(&tim); + char *time_string = asctime(now); + if (time_string) { + size_t time_len = strlen(time_string); + if (time_len > 0) { + time_string[time_len-1] = 0; // trim off terminating \n + } + } + LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<") + << " : " << time_string; + + bool indent; + int start = 0, nest = 3; + for (int i = 0; i < len; i += 1) { + if (buf[i] == '>') { + if ((i > 0) && (buf[i-1] == '/')) { + indent = false; + } else if ((start + 1 < len) && (buf[start + 1] == '/')) { + indent = false; + nest -= 2; + } else { + indent = true; + } + + // Output a tag + LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start); + + if (indent) + nest += 2; + + // Note if it's a PLAIN auth tag + if (IsAuthTag(buf + start, i + 1 - start)) { + censor_password_ = true; + } + + // incr + start = i + 1; + } + + if (buf[i] == '<' && start < i) { + if (censor_password_) { + LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##"; + censor_password_ = false; + } + else { + LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start); + } + start = i; + } + } + len = len - start; + memcpy(buf, buf + start, len); + *plen = len; + } + } + +}; + +static DebugLog debug_log_; + + +class FileShareClient : public sigslot::has_slots<>, public talk_base::MessageHandler { + public: + FileShareClient(buzz::XmppClient *xmppclient, const buzz::Jid &send_to, const cricket::FileShareManifest *manifest, std::string root_dir) : + xmpp_client_(xmppclient), + root_dir_(root_dir), + send_to_jid_(send_to), + waiting_for_file_(send_to == buzz::JID_EMPTY), + manifest_(manifest) {} + + void OnStateChange(buzz::XmppEngine::State state) { + switch (state) { + case buzz::XmppEngine::STATE_START: + std::cout << "Connecting..." << std::endl; + break; + case buzz::XmppEngine::STATE_OPENING: + std::cout << "Logging in. " << std::endl; + break; + case buzz::XmppEngine::STATE_OPEN: + std::cout << "Logged in as " << xmpp_client_->jid().Str() << std::endl; + if (!waiting_for_file_) + std::cout << "Waiting for " << send_to_jid_.Str() << std::endl; + OnSignon(); + break; + case buzz::XmppEngine::STATE_CLOSED: + std::cout << "Logged out." << std::endl; + break; + } + } + + private: + + enum { + MSG_STOP, + }; + + void OnJingleInfo(const std::string & relay_token, + const std::vector<std::string> &relay_addresses, + const std::vector<talk_base::SocketAddress> &stun_addresses) { + port_allocator_->SetStunHosts(stun_addresses); + port_allocator_->SetRelayHosts(relay_addresses); + port_allocator_->SetRelayToken(relay_token); + } + + + void OnStatusUpdate(const buzz::Status &status) { + if (status.available() && status.fileshare_capability()) { + + // A contact's status has changed. If the person we're looking for is online and able to receive + // files, send it. + if (send_to_jid_.BareEquals(status.jid())) { + std::cout << send_to_jid_.Str() << " has signed on." << std::endl; + cricket::FileShareSession* share = file_share_session_client_->CreateFileShareSession(); + share->Share(status.jid(), const_cast<cricket::FileShareManifest*>(manifest_)); + send_to_jid_ = buzz::Jid(""); + } + + } + } + + void OnMessage(talk_base::Message *m) { + ASSERT(m->message_id == MSG_STOP); + talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread(); + delete session_; + thread->Stop(); + } + + std::string filesize_to_string(unsigned int size) { + double size_display; + std::string format; + std::stringstream ret; + + // the comparisons to 1000 * (2^(n10)) are intentional + // it's so you don't see something like "1023 bytes", + // instead you'll see ".9 KB" + + if (size < 1000) { + format = "Bytes"; + size_display = size; + } else if (size < 1000 * 1024) { + format = "KiB"; + size_display = (double)size / 1024.0; + } else if (size < 1000 * 1024 * 1024) { + format = "MiB"; + size_display = (double)size / (1024.0 * 1024.0); + } else { + format = "GiB"; + size_display = (double)size / (1024.0 * 1024.0 * 1024.0); + } + + ret << std::setprecision(1) << std::setiosflags(std::ios::fixed) << size_display << " " << format; + return ret.str(); + } + + void OnSessionState(cricket::FileShareState state) { + talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread(); + std::stringstream manifest_description; + + switch(state) { + case cricket::FS_OFFER: + + // The offer has been made; print a summary of it and, if it's an incoming transfer, accept it + + if (manifest_->size() == 1) + manifest_description << session_->manifest()->item(0).name; + else if (session_->manifest()->GetFileCount() && session_->manifest()->GetFolderCount()) + manifest_description << session_->manifest()->GetFileCount() << " files and " << + session_->manifest()->GetFolderCount() << " directories"; + else if (session_->manifest()->GetFileCount() > 0) + manifest_description << session_->manifest()->GetFileCount() << " files"; + else + manifest_description << session_->manifest()->GetFolderCount() << " directories"; + + size_t filesize; + if (!session_->GetTotalSize(filesize)) { + manifest_description << " (Unknown size)"; + } else { + manifest_description << " (" << filesize_to_string(filesize) << ")"; + } + if (session_->is_sender()) { + std::cout << "Offering " << manifest_description.str() << " to " << send_to_jid_.Str() << std::endl; + } else if (waiting_for_file_) { + std::cout << "Receiving " << manifest_description.str() << " from " << session_->jid().BareJid().Str() << std::endl; + session_->Accept(); + waiting_for_file_ = false; + + // If this were a graphical client, we might want to go through the manifest, look for images, + // and request previews. There are two ways to go about this: + // + // If we want to display the preview in a web browser (like the embedded IE control in Google Talk), we could call + // GetImagePreviewUrl on the session, with the image's index in the manifest, the size, and a pointer to the URL. + // This will cause the session to listen for HTTP requests on localhost, and set url to a localhost URL that any + // web browser can use to get the image preview: + // + // std::string url; + // session_->GetImagePreviewUrl(0, 100, 100, &url); + // url = std::string("firefox \"") + url + "\""; + // system(url.c_str()); + // + // Alternately, you could use libjingle's own HTTP code with the FileShareSession's SocketPool interface to + // write the image preview directly into a StreamInterface: + // + // talk_base::HttpClient *client = new talk_base::HttpClient("pcp", session_); + // std::string path; + // session_->GetItemNetworkPath(0,1,&path); + // + // client->request().verb = talk_base::HV_GET; + // client->request().path = path + "?width=100&height=100"; + // talk_base::FileStream *file = new talk_base::FileStream; + // file->Open("/home/username/foo.jpg", "wb"); + // client->response().document.reset(file); + // client->start(); + } + break; + case cricket::FS_TRANSFER: + std::cout << "File transfer started." << std::endl; + break; + case cricket::FS_COMPLETE: + thread->Post(this, MSG_STOP); + std::cout << std::endl << "File transfer completed." << std::endl; + break; + case cricket::FS_LOCAL_CANCEL: + case cricket::FS_REMOTE_CANCEL: + std::cout << std::endl << "File transfer cancelled." << std::endl; + thread->Post(this, MSG_STOP); + break; + case cricket::FS_FAILURE: + std::cout << std::endl << "File transfer failed." << std::endl; + thread->Post(this, MSG_STOP); + break; + } + } + + void OnUpdateProgress(cricket::FileShareSession *sess) { + // Progress has occured on the transfer; update the UI + + size_t totalsize, progress; + std::string itemname; + unsigned int width = 79; + +#ifndef WIN32 + struct winsize ws; + if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)) + width = ws.ws_col; +#endif + + if(sess->GetTotalSize(totalsize) && sess->GetProgress(progress) && sess->GetCurrentItemName(&itemname)) { + float percent = (float)progress / totalsize; + unsigned int progressbar_width = (width * 4) / 5; + + const char *filename = itemname.c_str(); + std::cout.put('\r'); + for (unsigned int l = 0; l < width; l++) { + if (l < percent * progressbar_width) + std::cout.put('#'); + else if (l > progressbar_width && l < progressbar_width + 1 + strlen(filename)) + std::cout.put(filename[l-(progressbar_width + 1)]); + else + std::cout.put(' '); + } + std::cout.flush(); + } + } + + void OnResampleImage(std::string path, int width, int height, talk_base::HttpTransaction *trans) { + + // The other side has requested an image preview. This is an asynchronous request. We should resize + // the image to the requested size,and send that to ResampleComplete(). For simplicity, here, we + // send back the original sized image. Note that because we don't recognize images in our manifest + // this will never be called in pcp + + // Even if you don't resize images, you should implement this method and connect to the + // SignalResampleImage signal, just to return an error. + + talk_base::FileStream *s = new talk_base::FileStream(); + if (s->Open(path.c_str(), "rb")) + session_->ResampleComplete(s, trans, true); + else { + delete s; + session_->ResampleComplete(NULL, trans, false); + } + } + + void OnFileShareSessionCreate(cricket::FileShareSession *sess) { + session_ = sess; + sess->SignalState.connect(this, &FileShareClient::OnSessionState); + sess->SignalNextFile.connect(this, &FileShareClient::OnUpdateProgress); + sess->SignalUpdateProgress.connect(this, &FileShareClient::OnUpdateProgress); + sess->SignalResampleImage.connect(this, &FileShareClient::OnResampleImage); + sess->SetLocalFolder(root_dir_); + } + + void OnSignon() { + std::string client_unique = xmpp_client_->jid().Str(); + cricket::InitRandom(client_unique.c_str(), client_unique.size()); + + buzz::PresencePushTask *presence_push_ = new buzz::PresencePushTask(xmpp_client_); + presence_push_->SignalStatusUpdate.connect(this, &FileShareClient::OnStatusUpdate); + presence_push_->Start(); + + buzz::Status my_status; + my_status.set_jid(xmpp_client_->jid()); + my_status.set_available(true); + my_status.set_show(buzz::Status::SHOW_ONLINE); + my_status.set_priority(0); + my_status.set_know_capabilities(true); + my_status.set_fileshare_capability(true); + my_status.set_is_google_client(true); + my_status.set_version("1.0.0.66"); + + buzz::PresenceOutTask* presence_out_ = + new buzz::PresenceOutTask(xmpp_client_); + presence_out_->Send(my_status); + presence_out_->Start(); + + port_allocator_.reset(new cricket::HttpPortAllocator(&network_manager_, "pcp")); + + session_manager_.reset(new cricket::SessionManager(port_allocator_.get(), NULL)); + + cricket::SessionManagerTask * session_manager_task = new cricket::SessionManagerTask(xmpp_client_, session_manager_.get()); + session_manager_task->EnableOutgoingMessages(); + session_manager_task->Start(); + + buzz::JingleInfoTask *jingle_info_task = new buzz::JingleInfoTask(xmpp_client_); + jingle_info_task->RefreshJingleInfoNow(); + jingle_info_task->SignalJingleInfo.connect(this, &FileShareClient::OnJingleInfo); + jingle_info_task->Start(); + + file_share_session_client_.reset(new cricket::FileShareSessionClient(session_manager_.get(), xmpp_client_->jid(), "pcp")); + file_share_session_client_->SignalFileShareSessionCreate.connect(this, &FileShareClient::OnFileShareSessionCreate); + session_manager_->AddClient(NS_GOOGLE_SHARE, file_share_session_client_.get()); + } + + talk_base::NetworkManager network_manager_; + talk_base::scoped_ptr<cricket::HttpPortAllocator> port_allocator_; + talk_base::scoped_ptr<cricket::SessionManager> session_manager_; + talk_base::scoped_ptr<cricket::FileShareSessionClient> file_share_session_client_; + buzz::XmppClient *xmpp_client_; + buzz::Jid send_to_jid_; + const cricket::FileShareManifest *manifest_; + cricket::FileShareSession *session_; + bool waiting_for_file_; + std::string root_dir_; +}; + +static unsigned int get_dir_size(const char *directory) { + unsigned int total = 0; + talk_base::DirectoryIterator iter; + talk_base::Pathname path; + path.AppendFolder(directory); + iter.Iterate(path.pathname()); + while (iter.Next()) { + if (iter.Name() == "." || iter.Name() == "..") + continue; + if (iter.IsDirectory()) { + path.AppendPathname(iter.Name()); + total += get_dir_size(path.pathname().c_str()); + } + else + total += iter.FileSize(); + } + return total; +} + +int main(int argc, char **argv) { + talk_base::PhysicalSocketServer ss; + int i; + bool debug = false; + bool send_mode = false; + char cwd[256]; + getcwd(cwd, sizeof(cwd)); + for (i = 1; i < argc && *argv[i] == '-'; i++) { + if (!strcmp(argv[i], "-d")) { + debug = true; + } else { + std::cout << "USAGE: " << argv[0] << " [-d][-h] [FILE1 FILE2 ... FILE#] [JID]" << std::endl; + std::cout << " To send files, specify a list of files to send, followed by the JID of the recipient" << std::endl; + std::cout << " To receive files, specify no files or JID" << std::endl; + std::cout << "COMMAND LINE ARGUMENTS" << std::endl; + std::cout << " -h -- Prints this help message" << std::endl; + std::cout << " -d -- Prints debug messages to stderr" << std::endl; + exit(0); + } + } + + if (debug) + talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE); + else + talk_base::LogMessage::LogToDebug(talk_base::LS_ERROR + 1); + + + talk_base::InitializeSSL(); + XmppPump pump; + buzz::Jid jid; + buzz::XmppClientSettings xcs; + talk_base::InsecureCryptStringImpl pass; + std::string username; + + std::cout << "JID: "; + std::cin >> username; + jid = buzz::Jid(username); + if (!jid.IsValid() || jid.node() == "") { + printf("Invalid JID. JIDs should be in the form user@domain\n"); + return 1; + } + SetConsoleEcho(false); + std::cout << "Password: "; + std::cin >> pass.password(); + SetConsoleEcho(true); + std::cout << std::endl; + + xcs.set_user(jid.node()); + xcs.set_resource("pcp"); + xcs.set_host(jid.domain()); + xcs.set_use_tls(true); + + xcs.set_pass(talk_base::CryptString(pass)); + xcs.set_server(talk_base::SocketAddress("talk.google.com", 5222)); + + talk_base::Thread main_thread(&ss); + talk_base::ThreadManager::SetCurrent(&main_thread); + + if (debug) { + pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input); + pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output); + } + + cricket::FileShareManifest *manifest = new cricket::FileShareManifest(); + + for (;i < argc - 1;i++) { + if (0) { + printf("%s is not a valid file\n", argv[i]); + continue; + } + send_mode = true; + + // Additionally, we should check for image files here, and call + // AddImage on the manifest with their file size and image size. + // The receiving client can then request previews of those images + if (talk_base::Filesystem::IsFolder(std::string(argv[i]))) { + manifest->AddFolder(argv[i], get_dir_size(argv[i])); + } else { + size_t size = 0; + talk_base::Filesystem::GetFileSize(std::string(argv[i]), &size); + manifest->AddFile(argv[i], size); + } + } + buzz::Jid j; + if (send_mode) + j = buzz::Jid(argv[argc-1]); + else + j = buzz::JID_EMPTY; + + FileShareClient fs_client(pump.client(), j, manifest, cwd); + + pump.client()->SignalStateChange.connect(&fs_client, &FileShareClient::OnStateChange); + + pump.DoLogin(xcs, new XmppSocket(true), NULL); + main_thread.Run(); + pump.DoDisconnect(); + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/libjingle.sln b/Plugins/jingle/libjingle/talk/libjingle.sln new file mode 100644 index 0000000..fe0cf35 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/libjingle.sln @@ -0,0 +1,37 @@ +Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual C++ Express 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjingle", "libjingle.vcproj", "{DC948D76-8503-490C-A07D-11044004FCE3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcp", "examples\pcp\pcp.vcproj", "{EF5C9C17-874A-44C3-93CB-1165F03DBCD3}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "call", "examples\call\call.vcproj", "{A4132D45-BAE2-40E5-AC7C-C3C44FB24325}"
+ ProjectSection(ProjectDependencies) = postProject
+ {DC948D76-8503-490C-A07D-11044004FCE3} = {DC948D76-8503-490C-A07D-11044004FCE3}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Debug|Win32.Build.0 = Debug|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.ActiveCfg = Release|Win32
+ {DC948D76-8503-490C-A07D-11044004FCE3}.Release|Win32.Build.0 = Release|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Debug|Win32.Build.0 = Debug|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Release|Win32.ActiveCfg = Release|Win32
+ {EF5C9C17-874A-44C3-93CB-1165F03DBCD3}.Release|Win32.Build.0 = Release|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Debug|Win32.Build.0 = Debug|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Release|Win32.ActiveCfg = Release|Win32
+ {A4132D45-BAE2-40E5-AC7C-C3C44FB24325}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Plugins/jingle/libjingle/talk/libjingle.vcproj b/Plugins/jingle/libjingle/talk/libjingle.vcproj new file mode 100644 index 0000000..514bfbc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/libjingle.vcproj @@ -0,0 +1,1014 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="libjingle"
+ ProjectGUID="{DC948D76-8503-490C-A07D-11044004FCE3}"
+ RootNamespace="libjingle"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\include;C:\Desenvolvimento\linphone\lin\linphone\oRTP\include"
+ PreprocessorDefinitions="ENABLE_DEBUG;_DEBUG;_WINDOWS;WIN32;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;PRODUCTION_BUILD;PRODUCTION;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL;NO_ATL;HAVE_SPEEX;HAVE_ILBC"
+ ExceptionHandling="1"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ StructMemberAlignment="0"
+ TreatWChar_tAsBuiltInType="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..;C:\Desenvolvimento\linphone\lin\linphone\mediastreamer2\include;C:\Desenvolvimento\linphone\lin\linphone\oRTP\include"
+ PreprocessorDefinitions="_WINDOWS;WIN32;UNICODE;_UNICODE;WINVER=0x0500;_WIN32_WINNT=0x500;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;PRODUCTION_BUILD;PRODUCTION;_SCL_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;XML_STATIC;FEATURE_ENABLE_SSL;FEATURE_ENABLE_CHAT_ARCHIVING;FEATURE_ENABLE_VOICEMAIL;NO_ATL;HAVE_SPEEX;HAVE_ILBC"
+ ExceptionHandling="1"
+ RuntimeLibrary="0"
+ TreatWChar_tAsBuiltInType="false"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <Filter
+ Name="XMLLite"
+ >
+ <File
+ RelativePath=".\xmllite\qname.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlconstants.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlelement.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlprinter.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="XMPP"
+ >
+ <File
+ RelativePath=".\xmpp\constants.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\jid.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\ratelimitmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslmechanism.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlnsstack.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\base\asynchttprequest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncpacketsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asynctcpsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncudpsocket.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\autodetectproxy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\base64.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\bytebuffer.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\common.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\diskcache.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\diskcache_win32.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\fileutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\firewallsocketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\helpers.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\base\host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpbase.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpcommon.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\httpserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\logging.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\md5c.c"
+ >
+ </File>
+ <File
+ RelativePath=".\base\messagequeue.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\network.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\pathutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\physicalsocketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxydetect.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxyinfo.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\schanneladapter.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\signalthread.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketadapters.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddress.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketpool.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\ssladapter.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stream.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\streamutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringdigest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringencode.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringutils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\tarstream.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\task.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\taskrunner.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\thread.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\time.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32filesystem.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32socketserver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winfirewall.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winping.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="p2p"
+ >
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\p2p\base\constants.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2ptransport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2ptransportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\port.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\pseudotcp.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\rawtransport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\rawtransportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\session.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stun.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunrequest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\tcpport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transport.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transportchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\transportchannelproxy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\udpport.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="client"
+ >
+ <File
+ RelativePath=".\p2p\client\basicportallocator.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\httpportallocator.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\socketmonitor.cc"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="session"
+ >
+ <Filter
+ Name="tunnel"
+ >
+ <File
+ RelativePath=".\session\tunnel\tunnelsessionclient.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="phone"
+ >
+ <File
+ RelativePath=".\session\phone\audiomonitor.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\call.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\channelmanager.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\linphonemediaengine.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\phonesessionclient.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\voicechannel.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="fileshare"
+ >
+ <File
+ RelativePath=".\session\fileshare\fileshare.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\fileshare\fileshare.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <Filter
+ Name="XMLLite"
+ >
+ <File
+ RelativePath=".\xmllite\qname.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlconstants.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlelement.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlnsstack.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmllite\xmlprinter.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="XMPP"
+ >
+ <File
+ RelativePath=".\xmpp\asyncsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\constants.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\jid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\plainsaslhandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\prexmppauth.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslcookiemechanism.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslhandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslmechanism.h"
+ >
+ </File>
+ <File
+ RelativePath=".\xmpp\saslplainmechanism.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\base\asyncfile.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncpacketsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asynctcpsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\asyncudpsocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\base64.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\basicdefs.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\basictypes.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\bytebuffer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\byteorder.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\common.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\criticalsection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\helpers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\linked_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\logging.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\md5.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\messagequeue.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\network.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\physicalsocketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\proxyinfo.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\schanneladapter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\scoped_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\sec_buffer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\sigslot.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketadapters.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddress.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketaddresspair.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketfactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\socketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\ssladapter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stl_decl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringdigest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringencode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\stringutils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\task.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\taskrunner.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\thread.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\time.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\urlencode.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\win32socketserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winfirewall.h"
+ >
+ </File>
+ <File
+ RelativePath=".\base\winping.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="p2p"
+ >
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath=".\p2p\base\candidate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\helpers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\p2psocket.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\port.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\portallocator.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\pseudotcp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\relayserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\session.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessiondescription.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionmessagefactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\sessionresponsemessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\socketmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stun.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunrequest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\stunserver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\tcpport.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\base\udpport.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="client"
+ >
+ <File
+ RelativePath=".\p2p\client\basicportallocator.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\sessionclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\socketmonitor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionmessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionmessagefactory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\p2p\client\xmppsessionresponsemessage.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="session"
+ >
+ <File
+ RelativePath=".\session\receiver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\sessionsendtask.h"
+ >
+ </File>
+ <Filter
+ Name="tunnel"
+ >
+ <File
+ RelativePath=".\session\tunnel\pseudotcpchannel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\session\tunnel\tunnelsessionclient.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="phone"
+ >
+ <File
+ RelativePath=".\session\phone\audiomonitor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\call.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\channelmanager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\linphonemediaengine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\mediachannel.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\mediaengine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\phonesessionclient.h"
+ >
+ </File>
+ <File
+ RelativePath=".\session\phone\voicechannel.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ 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>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Plugins/jingle/libjingle/talk/p2p/base/candidate.h b/Plugins/jingle/libjingle/talk/p2p/base/candidate.h new file mode 100644 index 0000000..ac41311 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/candidate.h @@ -0,0 +1,119 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CANDIDATE_H_ +#define _CANDIDATE_H_ + +#include <string> +#include <sstream> +#include "talk/base/socketaddress.h" + +namespace cricket { + +// Candidate for ICE based connection discovery. + +class Candidate { +public: + + const std::string & name() const { return name_; } + void set_name(const std::string & name) { name_ = name; } + + const std::string & protocol() const { return protocol_; } + void set_protocol(const std::string & protocol) { protocol_ = protocol; } + + const talk_base::SocketAddress & address() const { return address_; } + void set_address(const talk_base::SocketAddress & address) + { address_ = address; } + + const float preference() const { return preference_; } + void set_preference(const float preference) { preference_ = preference; } + const std::string preference_str() const { + std::ostringstream ost; + ost << preference_; + return ost.str(); + } + void set_preference_str(const std::string & preference) { + std::istringstream ist(preference); + ist >> preference_; + } + + const std::string & username() const { return username_; } + void set_username(const std::string & username) { username_ = username; } + + const std::string & password() const { return password_; } + void set_password(const std::string & password) { password_ = password; } + + const std::string & type() const { return type_; } + void set_type(const std::string & type) { type_ = type; } + + const std::string & network_name() const { return network_name_; } + void set_network_name(const std::string & network_name) { + network_name_ = network_name; + } + + // Candidates in a new generation replace those in the old generation. + uint32 generation() const { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + const std::string generation_str() const { + std::ostringstream ost; + ost << generation_; + return ost.str(); + } + void set_generation_str(const std::string& str) { + std::istringstream ist(str); + ist >> generation_; + } + + // Determines whether this candidate is equivalent to the given one. + bool IsEquivalent(const Candidate& c) const { + // We ignore the network name, since that is just debug information, and + // the preference, since that should be the same if the rest is (and it's + // a float so equality checking is always worrisome). + return (name_ == c.name_) && + (protocol_ == c.protocol_) && + (address_ == c.address_) && + (username_ == c.username_) && + (password_ == c.password_) && + (type_ == c.type_) && + (generation_ == c.generation_); + } + +private: + std::string name_; + std::string protocol_; + talk_base::SocketAddress address_; + float preference_; + std::string username_; + std::string password_; + std::string type_; + std::string network_name_; + uint32 generation_; +}; + +} // namespace cricket + +#endif // _CANDIDATE_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/common.h b/Plugins/jingle/libjingle/talk/p2p/base/common.h new file mode 100644 index 0000000..72f8cfa --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/common.h @@ -0,0 +1,36 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef CRICKET_P2P_BASE_COMMON_H__ +#define CRICKET_P2P_BASE_COMMON_H__ + +#include "talk/base/logging.h" + +// Common log description format for jingle messages +#define LOG_J(sev,obj) LOG(sev) << "Jingle:" << obj->ToString() << ": " + +#endif // CRICKET_P2P_BASE_COMMON_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/constants.cc b/Plugins/jingle/libjingle/talk/p2p/base/constants.cc new file mode 100644 index 0000000..13b9f2d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/constants.cc @@ -0,0 +1,62 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/constants.h" + +namespace cricket { + +const std::string NS_EMPTY(""); +const std::string NS_GOOGLESESSION("http://www.google.com/session"); +#ifdef FEATURE_ENABLE_VOICEMAIL +const std::string NS_GOOGLEVOICEMAIL("http://www.google.com/session/voicemail"); +#endif + +const buzz::QName QN_SESSION(true, NS_GOOGLESESSION, "session"); + +const buzz::QName QN_REDIRECT_TARGET(true, NS_GOOGLESESSION, "target"); +const buzz::QName QN_REDIRECT_COOKIE(true, NS_GOOGLESESSION, "cookie"); +const buzz::QName QN_REDIRECT_REGARDING(true, NS_GOOGLESESSION, "regarding"); + +#ifdef FEATURE_ENABLE_VOICEMAIL +const buzz::QName QN_VOICEMAIL_REGARDING(true, NS_GOOGLEVOICEMAIL, "regarding"); +#endif + +const buzz::QName QN_INITIATOR(true, NS_EMPTY, "initiator"); + +const buzz::QName QN_ADDRESS(true, cricket::NS_EMPTY, "address"); +const buzz::QName QN_PORT(true, cricket::NS_EMPTY, "port"); +const buzz::QName QN_NETWORK(true, cricket::NS_EMPTY, "network"); +const buzz::QName QN_GENERATION(true, cricket::NS_EMPTY, "generation"); +const buzz::QName QN_USERNAME(true, cricket::NS_EMPTY, "username"); +const buzz::QName QN_PASSWORD(true, cricket::NS_EMPTY, "password"); +const buzz::QName QN_PREFERENCE(true, cricket::NS_EMPTY, "preference"); +const buzz::QName QN_PROTOCOL(true, cricket::NS_EMPTY, "protocol"); + +// Legacy transport messages +const buzz::QName kQnLegacyCandidate(true, cricket::NS_GOOGLESESSION, + "candidate"); +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/constants.h b/Plugins/jingle/libjingle/talk/p2p/base/constants.h new file mode 100644 index 0000000..46315eb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/constants.h @@ -0,0 +1,70 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_CONSTANTS_H_ +#define _CRICKET_P2P_BASE_CONSTANTS_H_ + +#include <string> +#include "talk/xmllite/qname.h" + +// This file contains constants related to signaling that are used in various +// classes in this directory. + +namespace cricket { + +extern const std::string NS_EMPTY; +extern const std::string NS_GOOGLESESSION; +#ifdef FEATURE_ENABLE_VOICEMAIL +extern const std::string NS_GOOGLEVOICEMAIL; +#endif + +extern const buzz::QName QN_SESSION; + +extern const buzz::QName QN_REDIRECT_TARGET; +extern const buzz::QName QN_REDIRECT_COOKIE; +extern const buzz::QName QN_REDIRECT_REGARDING; +#ifdef FEATURE_ENABLE_VOICEMAIL +extern const buzz::QName QN_VOICEMAIL_REGARDING; +#endif + +extern const buzz::QName QN_INITIATOR; + +extern const buzz::QName QN_ADDRESS; +extern const buzz::QName QN_PORT; +extern const buzz::QName QN_NETWORK; +extern const buzz::QName QN_GENERATION; +extern const buzz::QName QN_USERNAME; +extern const buzz::QName QN_PASSWORD; +extern const buzz::QName QN_PREFERENCE; +extern const buzz::QName QN_PROTOCOL; + +// Legacy transport messages +extern const buzz::QName kQnLegacyCandidate; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_CONSTANTS_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc new file mode 100644 index 0000000..2221d8c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.cc @@ -0,0 +1,209 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/p2ptransport.h" +#include "talk/base/common.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/constants.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/p2ptransportchannel.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace { + +// We only allow usernames to be this many characters or fewer. +const size_t kMaxUsernameSize = 16; + +} // namespace + +namespace cricket { + +const std::string kNsP2pTransport("http://www.google.com/transport/p2p"); +const buzz::QName kQnP2pTransport(true, kNsP2pTransport, "transport"); +const buzz::QName kQnP2pCandidate(true, kNsP2pTransport, "candidate"); +const buzz::QName kQnP2pUnknownChannelName(true, kNsP2pTransport, + "unknown-channel-name"); + +P2PTransport::P2PTransport(SessionManager* session_manager) + : Transport(session_manager, kNsP2pTransport) { +} + +P2PTransport::~P2PTransport() { + DestroyAllChannels(); +} + +buzz::XmlElement* P2PTransport::CreateTransportOffer() { + return new buzz::XmlElement(kQnP2pTransport, true); +} + +buzz::XmlElement* P2PTransport::CreateTransportAnswer() { + return new buzz::XmlElement(kQnP2pTransport, true); +} + +bool P2PTransport::OnTransportOffer(const buzz::XmlElement* elem) { + ASSERT(elem->Name() == kQnP2pTransport); + // We don't support any options, so we ignore them. + return true; +} + +bool P2PTransport::OnTransportAnswer(const buzz::XmlElement* elem) { + ASSERT(elem->Name() == kQnP2pTransport); + // We don't support any options. We fail if any are given. The other side + // should know from our request that we expected an empty response. + return elem->FirstChild() == NULL; +} + +bool P2PTransport::OnTransportMessage(const buzz::XmlElement* msg, + const buzz::XmlElement* stanza) { + ASSERT(msg->Name() == kQnP2pTransport); + for (const buzz::XmlElement* elem = msg->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name() == kQnP2pCandidate) { + // Make sure this candidate is valid. + Candidate candidate; + if (!ParseCandidate(stanza, elem, &candidate)) + return false; + + ForwardChannelMessage(elem->Attr(buzz::QN_NAME), + new buzz::XmlElement(*elem)); + } + } + return true; +} + +bool P2PTransport::OnTransportError(const buzz::XmlElement* session_msg, + const buzz::XmlElement* error) { + ASSERT(error->Name().Namespace() == kNsP2pTransport); + if ((error->Name() == kQnP2pUnknownChannelName) + && error->HasAttr(buzz::QN_NAME)) { + std::string channel_name = error->Attr(buzz::QN_NAME); + if (HasChannel(channel_name)) { + SignalChannelGone(this, channel_name); + } + } + return true; +} + +void P2PTransport::OnTransportChannelMessages( + const std::vector<buzz::XmlElement*>& candidates) { + buzz::XmlElement* transport = + new buzz::XmlElement(kQnP2pTransport, true); + for (size_t i = 0; i < candidates.size(); ++i) + transport->AddElement(candidates[i]); + + std::vector<buzz::XmlElement*> elems; + elems.push_back(transport); + SignalTransportMessage(this, elems); +} + +bool P2PTransport::ParseCandidate(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + Candidate* candidate) { + // Check for all of the required attributes. + if (!elem->HasAttr(buzz::QN_NAME) || + !elem->HasAttr(QN_ADDRESS) || + !elem->HasAttr(QN_PORT) || + !elem->HasAttr(QN_USERNAME) || + !elem->HasAttr(QN_PREFERENCE) || + !elem->HasAttr(QN_PROTOCOL) || + !elem->HasAttr(QN_GENERATION)) { + return BadRequest(stanza, "candidate missing required attribute", NULL); + } + + // Make sure the channel named actually exists. + if (!HasChannel(elem->Attr(buzz::QN_NAME))) { + scoped_ptr<buzz::XmlElement> + extra_info(new buzz::XmlElement(kQnP2pUnknownChannelName)); + extra_info->AddAttr(buzz::QN_NAME, elem->Attr(buzz::QN_NAME)); + return BadRequest(stanza, "channel named in candidate does not exist", + extra_info.get()); + } + + // Parse the address given. + talk_base::SocketAddress address; + if (!ParseAddress(stanza, elem, &address)) + return false; + + candidate->set_name(elem->Attr(buzz::QN_NAME)); + candidate->set_address(address); + candidate->set_username(elem->Attr(QN_USERNAME)); + candidate->set_preference_str(elem->Attr(QN_PREFERENCE)); + candidate->set_protocol(elem->Attr(QN_PROTOCOL)); + candidate->set_generation_str(elem->Attr(QN_GENERATION)); + + // Check that the username is not too long and does not use any bad chars. + if (candidate->username().size() > kMaxUsernameSize) + return BadRequest(stanza, "candidate username is too long", NULL); + if (!IsBase64Encoded(candidate->username())) + return BadRequest(stanza, + "candidate username has non-base64 encoded characters", + NULL); + + // Look for the non-required attributes. + if (elem->HasAttr(QN_PASSWORD)) + candidate->set_password(elem->Attr(QN_PASSWORD)); + if (elem->HasAttr(buzz::QN_TYPE)) + candidate->set_type(elem->Attr(buzz::QN_TYPE)); + if (elem->HasAttr(QN_NETWORK)) + candidate->set_network_name(elem->Attr(QN_NETWORK)); + + return true; +} + +buzz::XmlElement* P2PTransport::TranslateCandidate(const Candidate& c) { + buzz::XmlElement* candidate = new buzz::XmlElement(kQnP2pCandidate); + candidate->SetAttr(buzz::QN_NAME, c.name()); + candidate->SetAttr(QN_ADDRESS, c.address().IPAsString()); + candidate->SetAttr(QN_PORT, c.address().PortAsString()); + candidate->SetAttr(QN_PREFERENCE, c.preference_str()); + candidate->SetAttr(QN_USERNAME, c.username()); + candidate->SetAttr(QN_PROTOCOL, c.protocol()); + candidate->SetAttr(QN_GENERATION, c.generation_str()); + if (c.password().size() > 0) + candidate->SetAttr(QN_PASSWORD, c.password()); + if (c.type().size() > 0) + candidate->SetAttr(buzz::QN_TYPE, c.type()); + if (c.network_name().size() > 0) + candidate->SetAttr(QN_NETWORK, c.network_name()); + return candidate; +} + +TransportChannelImpl* P2PTransport::CreateTransportChannel( + const std::string& name, const std::string &session_type) { + return new P2PTransportChannel( + name, session_type, this, session_manager()->port_allocator()); +} + +void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) { + delete channel; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h new file mode 100644 index 0000000..2027d4a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransport.h @@ -0,0 +1,86 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_P2PTRANSPORT_H_ +#define _CRICKET_P2P_BASE_P2PTRANSPORT_H_ + +#include "talk/p2p/base/transport.h" + +namespace cricket { + +class Candidate; + +// Xml names used to name this transport and create our elements +extern const std::string kNsP2pTransport; +extern const buzz::QName kQnP2pTransport; +extern const buzz::QName kQnP2pCandidate; + +class P2PTransport: public Transport { + public: + P2PTransport(SessionManager* session_manager); + virtual ~P2PTransport(); + + // Implements negotiation of the P2P protocol. + virtual buzz::XmlElement* CreateTransportOffer(); + virtual buzz::XmlElement* CreateTransportAnswer(); + virtual bool OnTransportOffer(const buzz::XmlElement* elem); + virtual bool OnTransportAnswer(const buzz::XmlElement* elem); + + // Forwards each candidate message to the appropriate channel. + virtual bool OnTransportMessage(const buzz::XmlElement* msg, + const buzz::XmlElement* stanza); + virtual bool OnTransportError(const buzz::XmlElement* session_msg, + const buzz::XmlElement* error); + + protected: + // Creates and destroys P2PTransportChannel. + virtual TransportChannelImpl* CreateTransportChannel(const std::string& name, const std::string &session_type); + virtual void DestroyTransportChannel(TransportChannelImpl* channel); + + // Sends a given set of channel messages, which each describe a candidate, + // to the other client as a single transport message. + void OnTransportChannelMessages( + const std::vector<buzz::XmlElement*>& candidates); + + private: + // Attempts to parse the given XML into a candidate. Returns true if the + // XML is valid. If not, we will signal an error. + bool ParseCandidate(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + Candidate* candidate); + + // Generates a XML element describing the given candidate. + buzz::XmlElement* TranslateCandidate(const Candidate& c); + + friend class P2PTransportChannel; + + DISALLOW_EVIL_CONSTRUCTORS(P2PTransport); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_P2PTRANSPORT_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc new file mode 100644 index 0000000..717ae70 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.cc @@ -0,0 +1,911 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <errno.h> + +#include <iostream> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/p2p/base/common.h" +#include "talk/p2p/base/p2ptransportchannel.h" + +namespace { + +// messages for queuing up work for ourselves +const uint32 MSG_SORT = 1; +const uint32 MSG_PING = 2; +const uint32 MSG_ALLOCATE = 3; + +// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) +// for pinging. When the socket is writable, we will use only 1 Kbps because +// we don't want to degrade the quality on a modem. These numbers should work +// well on a 28.8K modem, which is the slowest connection on which the voice +// quality is reasonable at all. +static const uint32 PING_PACKET_SIZE = 60 * 8; +static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms +static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms + +// If there is a current writable connection, then we will also try hard to +// make sure it is pinged at this rate. +static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit + +// The minimum improvement in MOS that justifies a switch. +static const double kMinImprovement = 10; + +// Amount of time that we wait when *losing* writability before we try doing +// another allocation. +static const int kAllocateDelay = 1 * 1000; // 1 second + +// We will try creating a new allocator from scratch after a delay of this +// length without becoming writable (or timing out). +static const int kAllocatePeriod = 20 * 1000; // 20 seconds + +cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port, + cricket::Port* origin_port) { + if (!origin_port) + return cricket::Port::ORIGIN_MESSAGE; + else if (port == origin_port) + return cricket::Port::ORIGIN_THIS_PORT; + else + return cricket::Port::ORIGIN_OTHER_PORT; +} + +// Compares two connections based only on static information about them. +int CompareConnectionCandidates(cricket::Connection* a, + cricket::Connection* b) { + // Combine local and remote preferences + ASSERT(a->local_candidate().preference() == a->port()->preference()); + ASSERT(b->local_candidate().preference() == b->port()->preference()); + double a_pref = a->local_candidate().preference() + * a->remote_candidate().preference(); + double b_pref = b->local_candidate().preference() + * b->remote_candidate().preference(); + + // Now check combined preferences. Lower values get sorted last. + if (a_pref > b_pref) + return 1; + if (a_pref < b_pref) + return -1; + + return 0; +} + +// Compare two connections based on their writability and static preferences. +int CompareConnections(cricket::Connection *a, cricket::Connection *b) { + // Sort based on write-state. Better states have lower values. + if (a->write_state() < b->write_state()) + return 1; + if (a->write_state() > b->write_state()) + return -1; + + // Compare the candidate information. + return CompareConnectionCandidates(a, b); +} + +// Wraps the comparison connection into a less than operator that puts higher +// priority writable connections first. +class ConnectionCompare { +public: + bool operator()(const cricket::Connection *ca, + const cricket::Connection *cb) { + cricket::Connection* a = const_cast<cricket::Connection*>(ca); + cricket::Connection* b = const_cast<cricket::Connection*>(cb); + + // Compare first on writability and static preferences. + int cmp = CompareConnections(a, b); + if (cmp > 0) + return true; + if (cmp < 0) + return false; + + // Otherwise, sort based on latency estimate. + return a->rtt() < b->rtt(); + + // Should we bother checking for the last connection that last received + // data? It would help rendezvous on the connection that is also receiving + // packets. + // + // TODO: Yes we should definitely do this. The TCP protocol gains + // efficiency by being used bidirectionally, as opposed to two separate + // unidirectional streams. This test should probably occur before + // comparison of local prefs (assuming combined prefs are the same). We + // need to be careful though, not to bounce back and forth with both sides + // trying to rendevous with the other. + } +}; + +// Determines whether we should switch between two connections, based first on +// static preferences and then (if those are equal) on latency estimates. +bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { + if (a_conn == b_conn) + return false; + + if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen + return true; + + int prefs_cmp = CompareConnections(a_conn, b_conn); + if (prefs_cmp < 0) + return true; + if (prefs_cmp > 0) + return false; + + return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; +} + +} // unnamed namespace + +namespace cricket { + +P2PTransportChannel::P2PTransportChannel(const std::string &name, + const std::string &session_type, + P2PTransport* transport, + PortAllocator *allocator) +: TransportChannelImpl(name, session_type), transport_(transport), + allocator_(allocator), worker_thread_(talk_base::Thread::Current()), + waiting_for_signaling_(false), error_(0), best_connection_(NULL), + pinging_started_(false), sort_dirty_(false), was_writable_(false), + was_timed_out_(true) { +} + +P2PTransportChannel::~P2PTransportChannel() { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; +} + +// Add the allocator session to our list so that we know which sessions +// are still active. +void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) { + session->set_generation(static_cast<uint32>(allocator_sessions_.size())); + allocator_sessions_.push_back(session); + + // We now only want to apply new candidates that we receive to the ports + // created by this new session because these are replacing those of the + // previous sessions. + ports_.clear(); + + session->SignalPortReady.connect(this, &P2PTransportChannel::OnPortReady); + session->SignalCandidatesReady.connect( + this, &P2PTransportChannel::OnCandidatesReady); + session->GetInitialPorts(); + if (pinging_started_) + session->StartGetAllPorts(); +} + +// Go into the state of processing candidates, and running in general +void P2PTransportChannel::Connect() { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Kick off an allocator session + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Post(this, MSG_PING); +} + +// Reset the socket, clear up any previous allocations and start over +void P2PTransportChannel::Reset() { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Get rid of all the old allocators. This should clean up everything. + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) + delete allocator_sessions_[i]; + + allocator_sessions_.clear(); + ports_.clear(); + connections_.clear(); + best_connection_ = NULL; + + // Forget about all of the candidates we got before. + remote_candidates_.clear(); + + // Revert to the initial state. + set_readable(false); + set_writable(false); + + // Reinitialize the rest of our state. + waiting_for_signaling_ = false; + pinging_started_ = false; + sort_dirty_ = false; + was_writable_ = false; + was_timed_out_ = true; + + // If we allocated before, start a new one now. + if (transport_->connect_requested()) + OnAllocate(); + + // Start pinging as the ports come in. + thread()->Clear(this); + thread()->Post(this, MSG_PING); +} + +// A new port is available, attempt to make connections for it +void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, + Port* port) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Set in-effect options on the new port + for (OptionMap::const_iterator it = options_.begin(); + it != options_.end(); + ++it) { + int val = port->SetOption(it->first, it->second); + if (val < 0) { + LOG_J(LS_WARNING, port) << "SetOption(" << it->first + << ", " << it->second + << ") failed: " << port->GetError(); + } + } + + // Remember the ports and candidates, and signal that candidates are ready. + // The session will handle this, and send an initiate/accept/modify message + // if one is pending. + + ports_.push_back(port); + port->SignalUnknownAddress.connect( + this, &P2PTransportChannel::OnUnknownAddress); + port->SignalDestroyed.connect(this, &P2PTransportChannel::OnPortDestroyed); + + // Attempt to create a connection from this new port to all of the remote + // candidates that we were given so far. + + std::vector<RemoteCandidate>::iterator iter; + for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); + ++iter) + CreateConnection(port, *iter, iter->origin_port(), false); + + SortConnections(); +} + +// A new candidate is available, let listeners know +void P2PTransportChannel::OnCandidatesReady( + PortAllocatorSession *session, const std::vector<Candidate>& candidates) { + for (size_t i = 0; i < candidates.size(); ++i) { + buzz::XmlElement* msg = transport_->TranslateCandidate(candidates[i]); + SignalChannelMessage(this, msg); + } +} + +// Handle stun packets +void P2PTransportChannel::OnUnknownAddress( + Port *port, const talk_base::SocketAddress &address, StunMessage *stun_msg, + const std::string &remote_username) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Port has received a valid stun packet from an address that no Connection + // is currently available for. See if the remote user name is in the remote + // candidate list. If it isn't return error to the stun request. + + const Candidate *candidate = NULL; + std::vector<RemoteCandidate>::iterator it; + for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) { + if ((*it).username() == remote_username) { + candidate = &(*it); + break; + } + } + if (candidate == NULL) { + // Don't know about this username, the request is bogus + // This sometimes happens if a binding response comes in before the ACCEPT + // message. It is totally valid; the retry state machine will try again. + + port->SendBindingErrorResponse(stun_msg, address, + STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS); + delete stun_msg; + return; + } + + // Check for connectivity to this address. Create connections + // to this address across all local ports. First, add this as a new remote + // address + + Candidate new_remote_candidate = *candidate; + new_remote_candidate.set_address(address); + //new_remote_candidate.set_protocol(port->protocol()); + + // This remote username exists. Now create connections using this candidate, + // and resort + + if (CreateConnections(new_remote_candidate, port, true)) { + // Send the pinger a successful stun response. + port->SendBindingResponse(stun_msg, address); + + // Update the list of connections since we just added another. We do this + // after sending the response since it could (in principle) delete the + // connection in question. + SortConnections(); + } else { + // Hopefully this won't occur, because changing a destination address + // shouldn't cause a new connection to fail + ASSERT(false); + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, + STUN_ERROR_REASON_SERVER_ERROR); + } + + delete stun_msg; +} + +// We received a candidate from the other side, make connections so we +// can try to use these remote candidates with our local candidates. +void P2PTransportChannel::OnChannelMessage(const buzz::XmlElement* msg) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + Candidate remote_candidate; + bool valid = transport_->ParseCandidate(NULL, msg, &remote_candidate); + ASSERT(valid); + + // Create connections to this remote candidate. + CreateConnections(remote_candidate, NULL, false); + + // Resort the connections list, which may have new elements. + SortConnections(); +} + +// Creates connections from all of the ports that we care about to the given +// remote candidate. The return value is true iff we created a connection from +// the origin port. +bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate, + Port* origin_port, + bool readable) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Add a new connection for this candidate to every port that allows such a + // connection (i.e., if they have compatible protocols) and that does not + // already have a connection to an equivalent candidate. We must be careful + // to make sure that the origin port is included, even if it was pruned, + // since that may be the only port that can create this connection. + + bool created = false; + + std::vector<Port *>::reverse_iterator it; + for (it = ports_.rbegin(); it != ports_.rend(); ++it) { + if (CreateConnection(*it, remote_candidate, origin_port, readable)) { + if (*it == origin_port) + created = true; + } + } + + if ((origin_port != NULL) && + find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { + if (CreateConnection(origin_port, remote_candidate, origin_port, readable)) + created = true; + } + + // Remember this remote candidate so that we can add it to future ports. + RememberRemoteCandidate(remote_candidate, origin_port); + + return created; +} + +// Setup a connection object for the local and remote candidate combination. +// And then listen to connection object for changes. +bool P2PTransportChannel::CreateConnection(Port* port, + const Candidate& remote_candidate, + Port* origin_port, + bool readable) { + // Look for an existing connection with this remote address. If one is not + // found, then we can create a new connection for this address. + Connection* connection = port->GetConnection(remote_candidate.address()); + if (connection != NULL) { + // It is not legal to try to change any of the parameters of an existing + // connection; however, the other side can send a duplicate candidate. + if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { + LOG(INFO) << "Attempt to change a remote candidate"; + return false; + } + } else { + Port::CandidateOrigin origin = GetOrigin(port, origin_port); + connection = port->CreateConnection(remote_candidate, origin); + if (!connection) + return false; + + connections_.push_back(connection); + connection->SignalReadPacket.connect( + this, &P2PTransportChannel::OnReadPacket); + connection->SignalStateChange.connect( + this, &P2PTransportChannel::OnConnectionStateChange); + connection->SignalDestroyed.connect( + this, &P2PTransportChannel::OnConnectionDestroyed); + } + + // If we are readable, it is because we are creating this in response to a + // ping from the other side. This will cause the state to become readable. + if (readable) + connection->ReceivedPing(); + + return true; +} + +// Maintain our remote candidate list, adding this new remote one. +void P2PTransportChannel::RememberRemoteCandidate( + const Candidate& remote_candidate, Port* origin_port) { + // Remove any candidates whose generation is older than this one. The + // presence of a new generation indicates that the old ones are not useful. + uint32 i = 0; + while (i < remote_candidates_.size()) { + if (remote_candidates_[i].generation() < remote_candidate.generation()) { + LOG(INFO) << "Pruning candidate from old generation: " + << remote_candidates_[i].address().ToString(); + remote_candidates_.erase(remote_candidates_.begin() + i); + } else { + i += 1; + } + } + + // Make sure this candidate is not a duplicate. + for (uint32 i = 0; i < remote_candidates_.size(); ++i) { + if (remote_candidates_[i].IsEquivalent(remote_candidate)) { + LOG(INFO) << "Duplicate candidate: " + << remote_candidate.address().ToString(); + return; + } + } + + // Try this candidate for all future ports. + remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port)); + + // We have some candidates from the other side, we are now serious about + // this connection. Let's do the StartGetAllPorts thing. + if (!pinging_started_) { + pinging_started_ = true; + for (size_t i = 0; i < allocator_sessions_.size(); ++i) { + if (!allocator_sessions_[i]->IsGettingAllPorts()) + allocator_sessions_[i]->StartGetAllPorts(); + } + } +} + +// Send data to the other side, using our best connection +int P2PTransportChannel::SendPacket(const char *data, size_t len) { + // This can get called on any thread that is convenient to write from! + if (best_connection_ == NULL) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = best_connection_->Send(data, len); + if (sent <= 0) { + ASSERT(sent < 0); + error_ = best_connection_->GetError(); + } + return sent; +} + +// Monitor connection states +void P2PTransportChannel::UpdateConnectionStates() { + uint32 now = talk_base::Time(); + + // We need to copy the list of connections since some may delete themselves + // when we call UpdateState. + for (uint32 i = 0; i < connections_.size(); ++i) + connections_[i]->UpdateState(now); +} + +// Prepare for best candidate sorting +void P2PTransportChannel::RequestSort() { + if (!sort_dirty_) { + worker_thread_->Post(this, MSG_SORT); + sort_dirty_ = true; + } +} + +// Sort the available connections to find the best one. We also monitor +// the number of available connections and the current state so that we +// can possibly kick off more allocators (for more connections). +void P2PTransportChannel::SortConnections() { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Make sure the connection states are up-to-date since this affects how they + // will be sorted. + UpdateConnectionStates(); + + // Any changes after this point will require a re-sort. + sort_dirty_ = false; + + // Get a list of the networks that we are using. + std::set<talk_base::Network*> networks; + for (uint32 i = 0; i < connections_.size(); ++i) + networks.insert(connections_[i]->port()->network()); + + // Find the best alternative connection by sorting. It is important to note + // that amongst equal preference, writable connections, this will choose the + // one whose estimated latency is lowest. So it is the only one that we + // need to consider switching to. + + ConnectionCompare cmp; + std::stable_sort(connections_.begin(), connections_.end(), cmp); + Connection* top_connection = NULL; + if (connections_.size() > 0) + top_connection = connections_[0]; + + // If necessary, switch to the new choice. + if (ShouldSwitch(best_connection_, top_connection)) + SwitchBestConnectionTo(top_connection); + + // We can prune any connection for which there is a writable connection on + // the same network with better or equal prefences. We leave those with + // better preference just in case they become writable later (at which point, + // we would prune out the current best connection). We leave connections on + // other networks because they may not be using the same resources and they + // may represent very distinct paths over which we can switch. + std::set<talk_base::Network*>::iterator network; + for (network = networks.begin(); network != networks.end(); ++network) { + Connection* primier = GetBestConnectionOnNetwork(*network); + if (!primier || (primier->write_state() != Connection::STATE_WRITABLE)) + continue; + + for (uint32 i = 0; i < connections_.size(); ++i) { + if ((connections_[i] != primier) && + (connections_[i]->port()->network() == *network) && + (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { + connections_[i]->Prune(); + } + } + } + + // Count the number of connections in the various states. + + int writable = 0; + int write_connect = 0; + int write_timeout = 0; + + for (uint32 i = 0; i < connections_.size(); ++i) { + switch (connections_[i]->write_state()) { + case Connection::STATE_WRITABLE: + ++writable; + break; + case Connection::STATE_WRITE_CONNECT: + ++write_connect; + break; + case Connection::STATE_WRITE_TIMEOUT: + ++write_timeout; + break; + default: + ASSERT(false); + } + } + + if (writable > 0) { + HandleWritable(); + } else if (write_connect > 0) { + HandleNotWritable(); + } else { + HandleAllTimedOut(); + } + + // Update the state of this channel. This method is called whenever the + // state of any connection changes, so this is a good place to do this. + UpdateChannelState(); + + // Notify of connection state change + SignalConnectionMonitor(this); +} + +// Track the best connection, and let listeners know +void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { + // Note: the previous best_connection_ may be destroyed by now, so don't + // use it. + best_connection_ = conn; + if (best_connection_) { + LOG_J(LS_VERBOSE, this) << "New best connection: " << conn->ToString(); + SignalRouteChange(this, best_connection_->remote_candidate().address()); + } +} + +void P2PTransportChannel::UpdateChannelState() { + // The Handle* functions already set the writable state. We'll just double- + // check it here. + bool writable = + (best_connection_ != NULL) && + (best_connection_->write_state() == Connection::STATE_WRITABLE); + ASSERT(writable == this->writable()); + + bool readable = false; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (connections_[i]->read_state() == Connection::STATE_READABLE) + readable = true; + } + set_readable(readable); +} + +// We checked the status of our connections and we had at least one that +// was writable, go into the writable state. +void P2PTransportChannel::HandleWritable() { + // + // One or more connections writable! + // + if (!writable()) { + for (uint32 i = 0; i < allocator_sessions_.size(); ++i) { + if (allocator_sessions_[i]->IsGettingAllPorts()) { + allocator_sessions_[i]->StopGetAllPorts(); + } + } + + // Stop further allocations. + thread()->Clear(this, MSG_ALLOCATE); + } + + // We're writable, obviously we aren't timed out + was_writable_ = true; + was_timed_out_ = false; + set_writable(true); +} + +// We checked the status of our connections and we didn't have any that +// were writable, go into the connecting state (kick off a new allocator +// session). +void P2PTransportChannel::HandleNotWritable() { + // + // No connections are writable but not timed out! + // + if (was_writable_) { + // If we were writable, let's kick off an allocator session immediately + was_writable_ = false; + OnAllocate(); + } + + // We were connecting, obviously not ALL timed out. + was_timed_out_ = false; + set_writable(false); +} + +// We checked the status of our connections and not only weren't they writable +// but they were also timed out, we really need a new allocator. +void P2PTransportChannel::HandleAllTimedOut() { + // + // No connections... all are timed out! + // + if (!was_timed_out_) { + // We weren't timed out before, so kick off an allocator now (we'll still + // be in the fully timed out state until the allocator actually gives back + // new ports) + OnAllocate(); + } + + // NOTE: we start was_timed_out_ in the true state so that we don't get + // another allocator created WHILE we are in the process of building up + // our first allocator. + was_timed_out_ = true; + was_writable_ = false; + set_writable(false); +} + +// If we have a best connection, return it, otherwise return top one in the +// list (later we will mark it best). +Connection* P2PTransportChannel::GetBestConnectionOnNetwork( + talk_base::Network* network) { + // If the best connection is on this network, then it wins. + if (best_connection_ && (best_connection_->port()->network() == network)) + return best_connection_; + + // Otherwise, we return the top-most in sorted order. + for (uint32 i = 0; i < connections_.size(); ++i) { + if (connections_[i]->port()->network() == network) + return connections_[i]; + } + + return NULL; +} + +// Handle any queued up requests +void P2PTransportChannel::OnMessage(talk_base::Message *pmsg) { + if (pmsg->message_id == MSG_SORT) + OnSort(); + else if (pmsg->message_id == MSG_PING) + OnPing(); + else if (pmsg->message_id == MSG_ALLOCATE) + OnAllocate(); + else + ASSERT(false); +} + +// Handle queued up sort request +void P2PTransportChannel::OnSort() { + // Resort the connections based on the new statistics. + SortConnections(); +} + +// Handle queued up ping request +void P2PTransportChannel::OnPing() { + // Make sure the states of the connections are up-to-date (since this affects + // which ones are pingable). + UpdateConnectionStates(); + + // Find the oldest pingable connection and have it do a ping. + Connection* conn = FindNextPingableConnection(); + if (conn) + conn->Ping(talk_base::Time()); + + // Post ourselves a message to perform the next ping. + uint32 delay = writable() ? WRITABLE_DELAY : UNWRITABLE_DELAY; + thread()->PostDelayed(delay, this, MSG_PING); +} + +// Is the connection in a state for us to even consider pinging the other side? +bool P2PTransportChannel::IsPingable(Connection* conn) { + // An unconnected connection cannot be written to at all, so pinging is out + // of the question. + if (!conn->connected()) + return false; + + if (writable()) { + // If we are writable, then we only want to ping connections that could be + // better than this one, i.e., the ones that were not pruned. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); + } else { + // If we are not writable, then we need to try everything that might work. + // This includes both connections that do not have write timeout as well as + // ones that do not have read timeout. A connection could be readable but + // be in write-timeout if we pruned it before. Since the other side is + // still pinging it, it very well might still work. + return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || + (conn->read_state() != Connection::STATE_READ_TIMEOUT); + } +} + +// Returns the next pingable connection to ping. This will be the oldest +// pingable connection unless we have a writable connection that is past the +// maximum acceptable ping delay. +Connection* P2PTransportChannel::FindNextPingableConnection() { + uint32 now = talk_base::Time(); + if (best_connection_ && + (best_connection_->write_state() == Connection::STATE_WRITABLE) && + (best_connection_->last_ping_sent() + + MAX_CURRENT_WRITABLE_DELAY <= now)) { + return best_connection_; + } + + Connection* oldest_conn = NULL; + uint32 oldest_time = 0xFFFFFFFF; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) { + if (connections_[i]->last_ping_sent() < oldest_time) { + oldest_time = connections_[i]->last_ping_sent(); + oldest_conn = connections_[i]; + } + } + } + return oldest_conn; +} + +// return the number of "pingable" connections +uint32 P2PTransportChannel::NumPingableConnections() { + uint32 count = 0; + for (uint32 i = 0; i < connections_.size(); ++i) { + if (IsPingable(connections_[i])) + count += 1; + } + return count; +} + +// When a connection's state changes, we need to figure out who to use as +// the best connection again. It could have become usable, or become unusable. +void P2PTransportChannel::OnConnectionStateChange(Connection *connection) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // We have to unroll the stack before doing this because we may be changing + // the state of connections while sorting. + RequestSort(); +} + +// When a connection is removed, edit it out, and then update our best +// connection. +void P2PTransportChannel::OnConnectionDestroyed(Connection *connection) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Note: the previous best_connection_ may be destroyed by now, so don't + // use it. + + // Remove this connection from the list. + std::vector<Connection*>::iterator iter = + find(connections_.begin(), connections_.end(), connection); + ASSERT(iter != connections_.end()); + connections_.erase(iter); + + LOG_J(LS_INFO, this) << "Removed connection (" + << static_cast<int>(connections_.size()) << " remaining)"; + + // If this is currently the best connection, then we need to pick a new one. + // The call to SortConnections will pick a new one. It looks at the current + // best connection in order to avoid switching between fairly similar ones. + // Since this connection is no longer an option, we can just set best to NULL + // and re-choose a best assuming that there was no best connection. + if (best_connection_ == connection) { + SwitchBestConnectionTo(NULL); + RequestSort(); + } +} + +// When a port is destroyed remove it from our list of ports to use for +// connection attempts. +void P2PTransportChannel::OnPortDestroyed(Port* port) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Remove this port from the list (if we didn't drop it already). + std::vector<Port*>::iterator iter = find(ports_.begin(), ports_.end(), port); + if (iter != ports_.end()) + ports_.erase(iter); + + LOG(INFO) << "Removed port from p2p socket: " + << static_cast<int>(ports_.size()) << " remaining"; +} + +// We data is available, let listeners know +void P2PTransportChannel::OnReadPacket(Connection *connection, + const char *data, size_t len) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + + // Let the client know of an incoming packet + + SignalReadPacket(this, data, len); +} + +// Set options on ourselves is simply setting options on all of our available +// port objects. +int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { + OptionMap::iterator it = options_.find(opt); + if (it == options_.end()) { + options_.insert(std::make_pair(opt, value)); + } else if (it->second == value) { + return 0; + } else { + it->second = value; + } + + for (uint32 i = 0; i < ports_.size(); ++i) { + int val = ports_[i]->SetOption(opt, value); + if (val < 0) { + // Because this also occurs deferred, probably no point in reporting an + // error + LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " + << ports_[i]->GetError(); + } + } + return 0; +} + +// Time for a new allocator, lets make sure we have a signalling channel +// to communicate candidates through first. +void P2PTransportChannel::OnAllocate() { + waiting_for_signaling_ = true; + SignalRequestSignaling(); +} + +// When the signalling channel is ready, we can really kick off the allocator +void P2PTransportChannel::OnSignalingReady() { + if (waiting_for_signaling_) { + waiting_for_signaling_ = false; + AddAllocatorSession(allocator_->CreateSession(name(), session_type())); + thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE); + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h new file mode 100644 index 0000000..bea1537 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/p2ptransportchannel.h @@ -0,0 +1,156 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// P2PTransportChannel wraps up the state management of the connection between +// two P2P clients. Clients have candidate ports for connecting, and +// connections which are combinations of candidates from each end (Alice and +// Bob each have candidates, one candidate from Alice and one candidate from +// Bob are used to make a connection, repeat to make many connections). +// +// When all of the available connections become invalid (non-writable), we +// kick off a process of determining more candidates and more connections. +// +#ifndef _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_ +#define _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_ + +#include <vector> +#include <string> +#include "talk/base/sigslot.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/transport.h" +#include "talk/p2p/base/transportchannelimpl.h" +#include "talk/p2p/base/p2ptransport.h" + +namespace cricket { + +// Adds the port on which the candidate originated. +class RemoteCandidate : public Candidate { + public: + RemoteCandidate(const Candidate& c, Port* origin_port) + : Candidate(c), origin_port_(origin_port) {} + + Port* origin_port() { return origin_port_; } + + private: + Port* origin_port_; +}; + +// P2PTransportChannel manages the candidates and connection process to keep +// two P2P clients connected to each other. +class P2PTransportChannel : public TransportChannelImpl, + public talk_base::MessageHandler { + public: + P2PTransportChannel(const std::string &name, + const std::string &session_type, + P2PTransport* transport, + PortAllocator *allocator); + virtual ~P2PTransportChannel(); + + // From TransportChannelImpl: + virtual Transport* GetTransport() { return transport_; } + virtual void Connect(); + virtual void Reset(); + virtual void OnSignalingReady(); + virtual void OnChannelMessage(const buzz::XmlElement* msg); + + // From TransportChannel: + virtual int SendPacket(const char *data, size_t len); + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError() { return error_; } + + // These are used by the connection monitor. + sigslot::signal1<P2PTransportChannel*> SignalConnectionMonitor; + const std::vector<Connection *>& connections() const { return connections_; } + Connection* best_connection() const { return best_connection_; } + + // Handler for internal messages. + virtual void OnMessage(talk_base::Message *pmsg); + + private: + void UpdateConnectionStates(); + void RequestSort(); + void SortConnections(); + void SwitchBestConnectionTo(Connection* conn); + void UpdateChannelState(); + void HandleWritable(); + void HandleNotWritable(); + void HandleAllTimedOut(); + Connection* GetBestConnectionOnNetwork(talk_base::Network* network); + bool CreateConnections(const Candidate &remote_candidate, Port* origin_port, + bool readable); + bool CreateConnection(Port* port, const Candidate& remote_candidate, + Port* origin_port, bool readable); + void RememberRemoteCandidate(const Candidate& remote_candidate, + Port* origin_port); + void OnUnknownAddress(Port *port, const talk_base::SocketAddress &addr, + StunMessage *stun_msg, + const std::string &remote_username); + void OnPortReady(PortAllocatorSession *session, Port* port); + void OnCandidatesReady(PortAllocatorSession *session, + const std::vector<Candidate>& candidates); + void OnConnectionStateChange(Connection *connection); + void OnConnectionDestroyed(Connection *connection); + void OnPortDestroyed(Port* port); + void OnAllocate(); + void OnReadPacket(Connection *connection, const char *data, size_t len); + void OnSort(); + void OnPing(); + bool IsPingable(Connection* conn); + Connection* FindNextPingableConnection(); + uint32 NumPingableConnections(); + PortAllocatorSession* allocator_session() { + return allocator_sessions_.back(); + } + void AddAllocatorSession(PortAllocatorSession* session); + + talk_base::Thread* thread() const { return worker_thread_; } + + P2PTransport* transport_; + PortAllocator *allocator_; + talk_base::Thread *worker_thread_; + bool waiting_for_signaling_; + int error_; + std::vector<PortAllocatorSession*> allocator_sessions_; + std::vector<Port *> ports_; + std::vector<Connection *> connections_; + Connection *best_connection_; + std::vector<RemoteCandidate> remote_candidates_; + bool pinging_started_; // indicates whether StartGetAllCandidates has been called + bool sort_dirty_; // indicates whether another sort is needed right now + bool was_writable_; + bool was_timed_out_; + typedef std::map<talk_base::Socket::Option, int> OptionMap; + OptionMap options_; + + DISALLOW_EVIL_CONSTRUCTORS(P2PTransportChannel); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_P2PTRANSPORTCHANNEL_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port.cc b/Plugins/jingle/libjingle/talk/p2p/base/port.cc new file mode 100644 index 0000000..b5395df --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/port.cc @@ -0,0 +1,926 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include <errno.h> + +#include <algorithm> +#include <iostream> +#include <vector> + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/base/scoped_ptr.h" +#include "talk/base/socketadapters.h" +#include "talk/p2p/base/common.h" +#include "talk/p2p/base/port.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcmp; +} +#endif + +namespace { + +// The length of time we wait before timing out readability on a connection. +const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds + +// The length of time we wait before timing out writability on a connection. +const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds + +// The length of time we wait before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds + +// The number of pings that must fail to respond before we become unwritable. +const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5; + +// This is the length of time that we wait for a ping response to come back. +const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds + +// Determines whether we have seen at least the given maximum number of +// pings fail to have a response. +inline bool TooManyFailures( + const std::vector<uint32>& pings_since_last_response, + uint32 maximum_failures, + uint32 rtt_estimate, + uint32 now) { + + // If we haven't sent that many pings, then we can't have failed that many. + if (pings_since_last_response.size() < maximum_failures) + return false; + + // Check if the window in which we would expect a response to the ping has + // already elapsed. + return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now; +} + +// Determines whether we have gone too long without seeing any response. +inline bool TooLongWithoutResponse( + const std::vector<uint32>& pings_since_last_response, + uint32 maximum_time, + uint32 now) { + + if (pings_since_last_response.size() == 0) + return false; + + return pings_since_last_response[0] + maximum_time < now; +} + +// We will restrict RTT estimates (when used for determining state) to be +// within a reasonable range. +const uint32 MINIMUM_RTT = 100; // 0.1 seconds +const uint32 MAXIMUM_RTT = 3000; // 3 seconds + +// When we don't have any RTT data, we have to pick something reasonable. We +// use a large value just in case the connection is really slow. +const uint32 DEFAULT_RTT = MAXIMUM_RTT; + +// Computes our estimate of the RTT given the current estimate and the number +// of data points on which it is based. +inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) { + if (rtt_data_points == 0) + return DEFAULT_RTT; + else + return talk_base::_max(MINIMUM_RTT, talk_base::_min(MAXIMUM_RTT, 2 * rtt)); +} + +// Weighting of the old rtt value to new data. +const int RTT_RATIO = 3; // 3 : 1 + +// The delay before we begin checking if this port is useless. +const int kPortTimeoutDelay = 30 * 1000; // 30 seconds + +const uint32 MSG_CHECKTIMEOUT = 1; +const uint32 MSG_DELETE = 1; + +} + +namespace cricket { + +static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" }; + +const char * ProtoToString(ProtocolType proto) { + return PROTO_NAMES[proto]; +} + +bool StringToProto(const char * value, ProtocolType& proto) { + for (size_t i=0; i<=PROTO_LAST; ++i) { + if (strcmp(PROTO_NAMES[i], value) == 0) { + proto = static_cast<ProtocolType>(i); + return true; + } + } + return false; +} + +std::string Port::agent_; +talk_base::ProxyInfo Port::proxy_; + +Port::Port(talk_base::Thread* thread, const std::string& type, + talk_base::SocketFactory* factory, talk_base::Network* network) + : thread_(thread), factory_(factory), type_(type), network_(network), + preference_(-1), lifetime_(LT_PRESTART), enable_port_packets_(false) { + + if (factory_ == NULL) + factory_ = thread_->socketserver(); + + set_username_fragment(CreateRandomString(16)); + set_password(CreateRandomString(16)); +} + +Port::~Port() { + // Delete all of the remaining connections. We copy the list up front + // because each deletion will cause it to be modified. + + std::vector<Connection*> list; + + AddressMap::iterator iter = connections_.begin(); + while (iter != connections_.end()) { + list.push_back(iter->second); + ++iter; + } + + for (uint32 i = 0; i < list.size(); i++) + delete list[i]; +} + +Connection* Port::GetConnection(const talk_base::SocketAddress& remote_addr) { + AddressMap::const_iterator iter = connections_.find(remote_addr); + if (iter != connections_.end()) + return iter->second; + else + return NULL; +} + +void Port::AddAddress(const talk_base::SocketAddress& address, + const std::string& protocol, + bool final) { + Candidate c; + c.set_name(name_); + c.set_type(type_); + c.set_protocol(protocol); + c.set_address(address); + c.set_preference(preference_); + c.set_username(username_frag_); + c.set_password(password_); + c.set_network_name(network_->name()); + c.set_generation(generation_); + candidates_.push_back(c); + + if (final) + SignalAddressReady(this); +} + +void Port::AddConnection(Connection* conn) { + connections_[conn->remote_candidate().address()] = conn; + conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); + SignalConnectionCreated(this, conn); +} + +void Port::OnReadPacket( + const char* data, size_t size, const talk_base::SocketAddress& addr) { + // If the user has enabled port packets, just hand this over. + if (enable_port_packets_) { + SignalReadPacket(this, data, size, addr); + return; + } + + // If this is an authenticated STUN request, then signal unknown address and + // send back a proper binding response. + StunMessage* msg; + std::string remote_username; + if (!GetStunMessage(data, size, addr, msg, remote_username)) { + LOG_J(LS_ERROR, this) << "Received non-STUN packet from unknown address (" + << addr.ToString() << ")"; + } else if (!msg) { + // STUN message handled already + } else if (msg->type() == STUN_BINDING_REQUEST) { + SignalUnknownAddress(this, addr, msg, remote_username); + } else { + LOG_J(LS_ERROR, this) << "Received unexpected STUN message type (" + << msg->type() << ") from unknown address (" + << addr.ToString() << ")"; + delete msg; + } +} + +void Port::SendBindingRequest(Connection* conn) { + + // Construct the request message. + + StunMessage request; + request.SetType(STUN_BINDING_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = conn->remote_candidate().username(); + username.append(username_frag_); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request.AddAttribute(username_attr); + + // Send the request message. + // NOTE: If we wanted to, this is where we would add the HMAC. + talk_base::ByteBuffer buf; + request.Write(&buf); + SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false); +} + +bool Port::GetStunMessage(const char* data, size_t size, + const talk_base::SocketAddress& addr, + StunMessage *& msg, std::string& remote_username) { + // NOTE: This could clearly be optimized to avoid allocating any memory. + // However, at the data rates we'll be looking at on the client side, + // this probably isn't worth worrying about. + + msg = 0; + + // Parse the request message. If the packet is not a complete and correct + // STUN message, then ignore it. + talk_base::scoped_ptr<StunMessage> stun_msg(new StunMessage()); + talk_base::ByteBuffer buf(data, size); + if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { + return false; + } + + // The packet must include a username that either begins or ends with our + // fragment. It should begin with our fragment if it is a request and it + // should end with our fragment if it is a response. + const StunByteStringAttribute* username_attr = + stun_msg->GetByteString(STUN_ATTR_USERNAME); + + int remote_frag_len = (username_attr ? username_attr->length() : 0); + remote_frag_len -= static_cast<int>(username_frag_.size()); + + if (stun_msg->type() == STUN_BINDING_REQUEST) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes(), + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG_J(LS_ERROR, this) << "Received STUN request with bad username"; + SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + return true; + } + + remote_username.assign(username_attr->bytes() + username_frag_.size(), + username_attr->bytes() + username_attr->length()); + } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) + || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { + if ((remote_frag_len < 0) + || (std::memcmp(username_attr->bytes() + remote_frag_len, + username_frag_.c_str(), username_frag_.size()) != 0)) { + LOG_J(LS_ERROR, this) << "Received STUN response with bad username"; + // Do not send error response to a response + return true; + } + + remote_username.assign(username_attr->bytes(), + username_attr->bytes() + remote_frag_len); + + if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { + if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { + LOG_J(LS_ERROR, this) << "Received STUN binding error:" + << " class=" << error_code->error_class() + << " number=" << error_code->number() + << " reason='" << error_code->reason() << "'"; + // Return message to allow error-specific processing + } else { + LOG_J(LS_ERROR, this) + << "Received STUN error response with no error code"; + // Drop corrupt message + return true; + } + } + } else { + LOG_J(LS_ERROR, this) << "Received STUN packet with invalid type (" + << stun_msg->type() << ")"; + return true; + } + + // Return the STUN message found. + msg = stun_msg.release(); + return true; +} + +void Port::SendBindingResponse( + StunMessage* request, const talk_base::SocketAddress& addr) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetPort(addr.port()); + addr_attr->SetIP(addr.ip()); + response.AddAttribute(addr_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + talk_base::ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); + + // The fact that we received a successful request means that this connection + // (if one exists) should now be readable. + Connection* conn = GetConnection(addr); + assert(conn); + if (conn) + conn->ReceivedPing(); +} + +void Port::SendBindingErrorResponse( + StunMessage* request, const talk_base::SocketAddress& addr, int error_code, + const std::string& reason) { + + assert(request->type() == STUN_BINDING_REQUEST); + + // Retrieve the username from the request. If it didn't have one, we + // shouldn't be responding at all. + const StunByteStringAttribute* username_attr = + request->GetByteString(STUN_ATTR_USERNAME); + assert(username_attr); + + // Fill in the response message. + + StunMessage response; + response.SetType(STUN_BINDING_ERROR_RESPONSE); + response.SetTransactionID(request->transaction_id()); + + StunByteStringAttribute* username2_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username2_attr->CopyBytes(username_attr->bytes(), username_attr->length()); + response.AddAttribute(username2_attr); + + StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode(); + error_attr->SetErrorCode(error_code); + error_attr->SetReason(reason); + response.AddAttribute(error_attr); + + // Send the response message. + // NOTE: If we wanted to, this is where we would add the HMAC. + talk_base::ByteBuffer buf; + response.Write(&buf); + SendTo(buf.Data(), buf.Length(), addr, false); +} + +talk_base::AsyncPacketSocket* Port::CreatePacketSocket(ProtocolType proto) { + if (proto == PROTO_UDP) { + return new talk_base::AsyncUDPSocket( + factory_->CreateAsyncSocket(SOCK_DGRAM)); + } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) { + talk_base::AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM); + switch (proxy().type) { + case talk_base::PROXY_NONE: + break; + case talk_base::PROXY_SOCKS5: + socket = new talk_base::AsyncSocksProxySocket( + socket, proxy().address, proxy().username, proxy().password); + break; + case talk_base::PROXY_HTTPS: + default: + socket = new talk_base::AsyncHttpsProxySocket( + socket, user_agent(), proxy().address, proxy().username, + proxy().password); + break; + } + if (proto == PROTO_SSLTCP) { + socket = new talk_base::AsyncSSLSocket(socket); + } + return new talk_base::AsyncTCPSocket(socket); + } else { + LOG_J(LS_INFO, this) << "Unknown protocol (" << proto << ")"; + return 0; + } +} + +void Port::OnMessage(talk_base::Message *pmsg) { + assert(pmsg->message_id == MSG_CHECKTIMEOUT); + assert(lifetime_ == LT_PRETIMEOUT); + lifetime_ = LT_POSTTIMEOUT; + CheckTimeout(); +} + +std::string Port::ToString() const { + std::stringstream ss; + ss << "Port[" << name_ << ":" << type_ << ":" << network_->ToString() << "]"; + return ss.str(); +} + +void Port::EnablePortPackets() { + enable_port_packets_ = true; +} + +void Port::Start() { + // The port sticks around for a minimum lifetime, after which + // we destroy it when it drops to zero connections. + if (lifetime_ == LT_PRESTART) { + lifetime_ = LT_PRETIMEOUT; + thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT); + } else { + LOG_J(LS_WARNING, this) << "Port restart attempted"; + } +} + +void Port::OnConnectionDestroyed(Connection* conn) { + AddressMap::iterator iter = connections_.find(conn->remote_candidate().address()); + assert(iter != connections_.end()); + connections_.erase(iter); + + CheckTimeout(); +} + +void Port::Destroy() { + assert(connections_.empty()); + LOG_J(LS_INFO, this) << "Port deleted"; + SignalDestroyed(this); + delete this; +} + +void Port::CheckTimeout() { + // If this port has no connections, then there's no reason to keep it around. + // When the connections time out (both read and write), they will delete + // themselves, so if we have any connections, they are either readable or + // writable (or still connecting). + if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) { + Destroy(); + } +} + +// A ConnectionRequest is a simple STUN ping used to determine writability. +class ConnectionRequest : public StunRequest { +public: + ConnectionRequest(Connection* connection) : connection_(connection) { + } + + virtual ~ConnectionRequest() { + } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + std::string username = connection_->remote_candidate().username(); + username.append(connection_->port()->username_fragment()); + username_attr->CopyBytes(username.c_str(), (uint16)username.size()); + request->AddAttribute(username_attr); + } + + virtual void OnResponse(StunMessage* response) { + connection_->OnConnectionRequestResponse(response, Elapsed()); + } + + virtual void OnErrorResponse(StunMessage* response) { + connection_->OnConnectionRequestErrorResponse(response, Elapsed()); + } + + virtual void OnTimeout() { + } + + virtual int GetNextDelay() { + // Each request is sent only once. After a single delay , the request will + // time out. + timeout_ = true; + return CONNECTION_RESPONSE_TIMEOUT; + } + +private: + Connection* connection_; +}; + +// +// Connection +// + +Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate) + : requests_(port->thread()), port_(port), local_candidate_index_(index), + remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT), + write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false), + rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0), + recv_total_bytes_(0), recv_bytes_second_(0), + last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0), + sent_total_bytes_(0), sent_bytes_second_(0), + last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0), + reported_(false) { + + // Wire up to send stun packets + requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); +} + +Connection::~Connection() { +} + +const Candidate& Connection::local_candidate() const { + if (local_candidate_index_ < port_->candidates().size()) + return port_->candidates()[local_candidate_index_]; + assert(false); + static Candidate foo; + return foo; +} + +void Connection::set_read_state(ReadState value) { + ReadState old_value = read_state_; + read_state_ = value; + if (value != old_value) { + LOG_J(LS_VERBOSE, this) << "set_read_state"; + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_write_state(WriteState value) { + WriteState old_value = write_state_; + write_state_ = value; + if (value != old_value) { + LOG_J(LS_VERBOSE, this) << "set_write_state"; + SignalStateChange(this); + CheckTimeout(); + } +} + +void Connection::set_connected(bool value) { + bool old_value = connected_; + connected_ = value; + if (value != old_value) { + LOG_J(LS_VERBOSE, this) << "set_connected"; + } +} + +void Connection::OnSendStunPacket( + const void* data, size_t size, StunRequest* req) { + port_->SendTo(data, size, remote_candidate_.address(), false); +} + +void Connection::OnReadPacket(const char* data, size_t size) { + StunMessage* msg; + std::string remote_username; + const talk_base::SocketAddress& addr(remote_candidate_.address()); + if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) { + // The packet did not parse as a valid STUN message + + // If this connection is readable, then pass along the packet. + if (read_state_ == STATE_READABLE) { + // readable means data from this address is acceptable + // Send it on! + + recv_total_bytes_ += size; + SignalReadPacket(this, data, size); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + } else { + // Not readable means the remote address hasn't send a valid + // binding request yet. + + LOG_J(LS_WARNING, this) + << "Received non-STUN packet from an unreadable connection."; + } + } else if (!msg) { + // The packet was STUN, but was already handled + } else if (remote_username != remote_candidate_.username()) { + // Not destined this connection + LOG_J(LS_ERROR, this) << "Received STUN packet on wrong address."; + if (msg->type() == STUN_BINDING_REQUEST) { + port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, + STUN_ERROR_REASON_BAD_REQUEST); + } + delete msg; + } else { + // The packet is STUN, with the current username + // If this is a STUN request, then update the readable bit and respond. + // If this is a STUN response, then update the writable bit. + + switch (msg->type()) { + case STUN_BINDING_REQUEST: + // Incoming, validated stun request from remote peer. + // This call will also set the connection readable. + + port_->SendBindingResponse(msg, addr); + + // If timed out sending writability checks, start up again + if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) + set_write_state(STATE_WRITE_CONNECT); + break; + + case STUN_BINDING_RESPONSE: + case STUN_BINDING_ERROR_RESPONSE: + // Response from remote peer. Does it match request sent? + // This doesn't just check, it makes callbacks if transaction + // id's match + requests_.CheckResponse(msg); + break; + + default: + assert(false); + break; + } + + // Done with the message; delete + + delete msg; + } +} + +void Connection::Prune() { + if (!pruned_) { + LOG_J(LS_VERBOSE, this) << "Connection pruned"; + pruned_ = true; + requests_.Clear(); + set_write_state(STATE_WRITE_TIMEOUT); + } +} + +void Connection::Destroy() { + LOG_J(LS_VERBOSE, this) << "Connection destroyed"; + set_read_state(STATE_READ_TIMEOUT); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void Connection::UpdateState(uint32 now) { + // Check the readable state. + // + // Since we don't know how many pings the other side has attempted, the best + // test we can do is a simple window. + + if ((read_state_ == STATE_READABLE) && + (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) { + set_read_state(STATE_READ_TIMEOUT); + } + + // Check the writable state. (The order of these checks is important.) + // + // Before becoming unwritable, we allow for a fixed number of pings to fail + // (i.e., receive no response). We also have to give the response time to + // get back, so we include a conservative estimate of this. + // + // Before timing out writability, we give a fixed amount of time. This is to + // allow for changes in network conditions. + + uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_); + + if ((write_state_ == STATE_WRITABLE) && + TooManyFailures(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_FAILURES, + rtt, + now) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_CONNECT_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_CONNECT); + } + + if ((write_state_ == STATE_WRITE_CONNECT) && + TooLongWithoutResponse(pings_since_last_response_, + CONNECTION_WRITE_TIMEOUT, + now)) { + set_write_state(STATE_WRITE_TIMEOUT); + } +} + +void Connection::Ping(uint32 now) { + assert(connected_); + last_ping_sent_ = now; + pings_since_last_response_.push_back(now); + requests_.Send(new ConnectionRequest(this)); +} + +void Connection::ReceivedPing() { + last_ping_received_ = talk_base::Time(); + set_read_state(STATE_READABLE); +} + +std::string Connection::ToString() const { + const char CONNECT_STATE_ABBREV[2] = { + '-', // not connected (false) + 'C', // connected (true) + }; + const char READ_STATE_ABBREV[2] = { + 'R', // STATE_READABLE + '-', // STATE_READ_TIMEOUT + }; + const char WRITE_STATE_ABBREV[3] = { + 'W', // STATE_WRITABLE + 'w', // STATE_WRITE_CONNECT + '-', // STATE_WRITE_TIMEOUT + }; + const Candidate& local = local_candidate(); + const Candidate& remote = remote_candidate(); + std::stringstream ss; + ss << "Conn[" << local.generation() + << ":" << local.name() << ":" << local.type() << ":" << local.address().ToString() + << "->" << remote.name() << ":" << remote.type() << ":" << remote.address().ToString() + << "|" + << CONNECT_STATE_ABBREV[connected()] + << READ_STATE_ABBREV[read_state()] + << WRITE_STATE_ABBREV[write_state()] + << "]"; + return ss.str(); +} + +void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) { + // We have a potentially valid reply from the remote address. + // The packet must include a username that ends with our fragment, + // since it is a response. + + // Check exact message type + bool valid = true; + if (response->type() != STUN_BINDING_RESPONSE) + valid = false; + + // Must have username attribute + const StunByteStringAttribute* username_attr = + response->GetByteString(STUN_ATTR_USERNAME); + if (valid) { + if (!username_attr) { + LOG_J(LS_ERROR, this) << "Received likely STUN packet with no username"; + valid = false; + } + } + + // Length must be at least the size of our fragment (actually, should + // be bigger since our fragment is at the end!) + if (valid) { + if (username_attr->length() <= port_->username_fragment().size()) { + LOG_J(LS_ERROR, this) << "Received likely STUN packet with short username"; + valid = false; + } + } + + // Compare our fragment with the end of the username - must be exact match + if (valid) { + std::string username_fragment = port_->username_fragment(); + int offset = (int)(username_attr->length() - username_fragment.size()); + if (std::memcmp(username_attr->bytes() + offset, + username_fragment.c_str(), username_fragment.size()) != 0) { + LOG_J(LS_ERROR, this) << "Received STUN response with bad username"; + valid = false; + } + } + + if (valid) { + // Valid response. If we're not already, become writable. We may be + // bringing a pruned connection back to life, but if we don't really want + // it, we can always prune it again. + set_write_state(STATE_WRITABLE); + + pings_since_last_response_.clear(); + rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); + rtt_data_points_ += 1; + } +} + +void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) { + const StunErrorCodeAttribute* error = response->GetErrorCode(); + uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE; + + if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE) + || (error_code == STUN_ERROR_SERVER_ERROR) + || (error_code == STUN_ERROR_UNAUTHORIZED)) { + // Recoverable error, retry + } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) { + // Race failure, retry + } else { + // This is not a valid connection. + set_write_state(STATE_WRITE_TIMEOUT); + } +} + +void Connection::CheckTimeout() { + // If both read and write have timed out, then this connection can contribute + // no more to p2p socket unless at some later date readability were to come + // back. However, we gave readability a long time to timeout, so at this + // point, it seems fair to get rid of this connectoin. + if ((read_state_ == STATE_READ_TIMEOUT) && + (write_state_ == STATE_WRITE_TIMEOUT)) { + port_->thread()->Post(this, MSG_DELETE); + } +} + +void Connection::OnMessage(talk_base::Message *pmsg) { + assert(pmsg->message_id == MSG_DELETE); + + LOG_J(LS_INFO, this) << "Connection deleted"; + SignalDestroyed(this); + delete this; +} + +size_t Connection::recv_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = talk_base::Time(); + if (last_recv_bytes_second_time_ != (uint32)-1) { + int delta = talk_base::TimeDiff(current_time, last_recv_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta; + recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_recv_bytes_second_time_ = current_time - fraction_time; + last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes; + } + } + if (last_recv_bytes_second_time_ == (uint32)-1) { + last_recv_bytes_second_time_ = current_time; + last_recv_bytes_second_calc_ = recv_total_bytes_; + } + + return recv_bytes_second_; +} + +size_t Connection::recv_total_bytes() { + return recv_total_bytes_; +} + +size_t Connection::sent_bytes_second() { + // Snapshot bytes / second calculator + + uint32 current_time = talk_base::Time(); + if (last_sent_bytes_second_time_ != (uint32)-1) { + int delta = talk_base::TimeDiff(current_time, last_sent_bytes_second_time_); + if (delta >= 1000) { + int fraction_time = delta % 1000; + int seconds_time = delta - fraction_time; + int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta; + sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta; + last_sent_bytes_second_time_ = current_time - fraction_time; + last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes; + } + } + if (last_sent_bytes_second_time_ == (uint32)-1) { + last_sent_bytes_second_time_ = current_time; + last_sent_bytes_second_calc_ = sent_total_bytes_; + } + + return sent_bytes_second_; +} + +size_t Connection::sent_total_bytes() { + return sent_total_bytes_; +} + +ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate) + : Connection(port, index, candidate), error_(0) { +} + +int ProxyConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = port_->SendTo(data, size, remote_candidate_.address(), true); + if (sent <= 0) { + assert(sent < 0); + error_ = port_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port.h b/Plugins/jingle/libjingle/talk/p2p/base/port.h new file mode 100644 index 0000000..57c0727 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/port.h @@ -0,0 +1,421 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __PORT_H__ +#define __PORT_H__ + +#include "talk/base/network.h" +#include "talk/base/socketaddress.h" +#include "talk/base/proxyinfo.h" +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/candidate.h" +#include "talk/p2p/base/stun.h" +#include "talk/p2p/base/stunrequest.h" + +#include <string> +#include <vector> +#include <map> + +namespace talk_base { +class AsyncPacketSocket; +} + +namespace cricket { + +class Connection; + +enum ProtocolType { + PROTO_UDP, + PROTO_TCP, + PROTO_SSLTCP, + PROTO_LAST = PROTO_SSLTCP +}; + +const char * ProtoToString(ProtocolType proto); +bool StringToProto(const char * value, ProtocolType& proto); + +struct ProtocolAddress { + talk_base::SocketAddress address; + ProtocolType proto; + + ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p) + : address(a), proto(p) { } +}; + +// Represents a local communication mechanism that can be used to create +// connections to similar mechanisms of the other client. Subclasses of this +// one add support for specific mechanisms like local UDP ports. +class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { +public: + Port(talk_base::Thread* thread, const std::string &type, + talk_base::SocketFactory* factory, talk_base::Network* network); + virtual ~Port(); + + // The thread on which this port performs its I/O. + talk_base::Thread* thread() { return thread_; } + + // The factory used to create the sockets of this port. + talk_base::SocketFactory* socket_factory() const { return factory_; } + void set_socket_factory(talk_base::SocketFactory* factory) + { factory_ = factory; } + + // Each port is identified by a name (for debugging purposes). + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + // In order to establish a connection to this Port (so that real data can be + // sent through), the other side must send us a STUN binding request that is + // authenticated with this username and password. + const std::string& username_fragment() const { return username_frag_; } + const std::string& password() const { return password_; } + + // A value in [0,1] that indicates the preference for this port versus other + // ports on this client. (Larger indicates more preference.) + float preference() const { return preference_; } + void set_preference(float preference) { preference_ = preference; } + + // Identifies the port type. + //const std::string& protocol() const { return proto_; } + const std::string& type() const { return type_; } + + // Identifies network that this port was allocated on. + talk_base::Network* network() { return network_; } + + // Identifies the generation that this port was created in. + uint32 generation() { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + + // PrepareAddress will attempt to get an address for this port that other + // clients can send to. It may take some time before the address is read. + // Once it is ready, we will send SignalAddressReady. If errors are + // preventing the port from getting an address, it may send + // SignalAddressError. + virtual void PrepareAddress() = 0; + sigslot::signal1<Port*> SignalAddressReady; + sigslot::signal1<Port*> SignalAddressError; + + // Provides all of the above information in one handy object. + const std::vector<Candidate>& candidates() const { return candidates_; } + + // Returns a map containing all of the connections of this port, keyed by the + // remote address. + typedef std::map<talk_base::SocketAddress, Connection*> AddressMap; + const AddressMap& connections() { return connections_; } + + // Returns the connection to the given address or NULL if none exists. + Connection* GetConnection(const talk_base::SocketAddress& remote_addr); + + // Creates a new connection to the given address. + enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE }; + virtual Connection* CreateConnection(const Candidate& remote_candidate, + CandidateOrigin origin) = 0; + + // Called each time a connection is created. + sigslot::signal2<Port*, Connection*> SignalConnectionCreated; + + // Sends the given packet to the given address, provided that the address is + // that of a connection or an address that has sent to us already. + virtual int SendTo( + const void* data, size_t size, const talk_base::SocketAddress& addr, + bool payload) = 0; + + // Indicates that we received a successful STUN binding request from an + // address that doesn't correspond to any current connection. To turn this + // into a real connection, call CreateConnection. + sigslot::signal4<Port*, const talk_base::SocketAddress&, StunMessage*, + const std::string&> SignalUnknownAddress; + + // Sends a response message (normal or error) to the given request. One of + // these methods should be called as a response to SignalUnknownAddress. + // NOTE: You MUST call CreateConnection BEFORE SendBindingResponse. + void SendBindingResponse(StunMessage* request, + const talk_base::SocketAddress& addr); + void SendBindingErrorResponse( + StunMessage* request, const talk_base::SocketAddress& addr, + int error_code, const std::string& reason); + + // Indicates that errors occurred when performing I/O. + sigslot::signal2<Port*, int> SignalReadError; + sigslot::signal2<Port*, int> SignalWriteError; + + // Functions on the underlying socket(s). + virtual int SetOption(talk_base::Socket::Option opt, int value) = 0; + virtual int GetError() = 0; + + static void set_proxy(const std::string& user_agent, + const talk_base::ProxyInfo& proxy) { + agent_ = user_agent; proxy_ = proxy; + } + static const std::string& user_agent() { return agent_; } + static const talk_base::ProxyInfo& proxy() { return proxy_; } + + talk_base::AsyncPacketSocket * CreatePacketSocket(ProtocolType proto); + + // Normally, packets arrive through a connection (or they result signaling of + // unknown address). Calling this method turns off delivery of packets + // through their respective connection and instead delivers every packet + // through this port. + void EnablePortPackets(); + sigslot::signal4<Port*, const char*, size_t, const talk_base::SocketAddress&> + SignalReadPacket; + + // Indicates to the port that its official use has now begun. This will + // start the timer that checks to see if the port is being used. + void Start(); + + // Called if the port has no connections and is no longer useful. + void Destroy(); + + // Signaled when this port decides to delete itself because it no longer has + // any usefulness. + sigslot::signal1<Port*> SignalDestroyed; + + virtual void OnMessage(talk_base::Message *pmsg); + + // Debugging description of this port + std::string ToString() const; + +protected: + talk_base::Thread* thread_; + talk_base::SocketFactory* factory_; + std::string type_; + talk_base::Network* network_; + uint32 generation_; + std::string name_; + std::string username_frag_; + std::string password_; + float preference_; + std::vector<Candidate> candidates_; + AddressMap connections_; + enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_; + bool enable_port_packets_; + + // Fills in the username fragment and password. These will be initially set + // in the constructor to random values. Subclasses can override, though. + void set_username_fragment(const std::string& username_fragment) { + username_frag_ = username_fragment; + } + void set_password(const std::string& password) { password_ = password; } + + // Fills in the local address of the port. + void AddAddress(const talk_base::SocketAddress& address, + const std::string& protocol, bool final); + + // Adds the given connection to the list. (Deleting removes them.) + void AddConnection(Connection* conn); + + // Called when a packet is received from an unknown address that is not + // currently a connection. If this is an authenticated STUN binding request, + // then we will signal the client. + void OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& addr); + + // Constructs a STUN binding request for the given connection and sends it. + void SendBindingRequest(Connection* conn); + + // If the given data comprises a complete and correct STUN message then the + // return value is true, otherwise false. If the message username corresponds + // with this port's username fragment, msg will contain the parsed STUN + // message. Otherwise, the function may send a STUN response internally. + // remote_username contains the remote fragment of the STUN username. + bool GetStunMessage(const char* data, size_t size, + const talk_base::SocketAddress& addr, + StunMessage *& msg, std::string& remote_username); + + friend class Connection; + +private: + // Called when one of our connections deletes itself. + void OnConnectionDestroyed(Connection* conn); + + // Checks if this port is useless, and hence, should be destroyed. + void CheckTimeout(); + + static std::string agent_; + static talk_base::ProxyInfo proxy_; +}; + +// Represents a communication link between a port on the local client and a +// port on the remote client. +class Connection : public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + virtual ~Connection(); + + // The local port where this connection sends and receives packets. + Port* port() { return port_; } + const Port* port() const { return port_; } + + // Returns the description of the local port + virtual const Candidate& local_candidate() const; + + // Returns the description of the remote port to which we communicate. + const Candidate& remote_candidate() const { return remote_candidate_; } + + enum ReadState { + STATE_READABLE = 0, // we have received pings recently + STATE_READ_TIMEOUT = 1 // we haven't received pings in a while + }; + + ReadState read_state() const { return read_state_; } + + enum WriteState { + STATE_WRITABLE = 0, // we have received ping responses recently + STATE_WRITE_CONNECT = 1, // we have had a few ping failures + STATE_WRITE_TIMEOUT = 2 // we have had a large number of ping failures + }; + + WriteState write_state() const { return write_state_; } + + // Determines whether the connection has finished connecting. This can only + // be false for TCP connections. + bool connected() const { return connected_; } + + // Estimate of the round-trip time over this connection. + uint32 rtt() const { return rtt_; } + + size_t sent_total_bytes(); + size_t sent_bytes_second(); + size_t recv_total_bytes(); + size_t recv_bytes_second(); + sigslot::signal1<Connection*> SignalStateChange; + + // Sent when the connection has decided that it is no longer of value. It + // will delete itself immediately after this call. + sigslot::signal1<Connection*> SignalDestroyed; + + // The connection can send and receive packets asynchronously. This matches + // the interface of AsyncPacketSocket, which may use UDP or TCP under the + // covers. + virtual int Send(const void* data, size_t size) = 0; + + // Error if Send() returns < 0 + virtual int GetError() = 0; + + sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket; + + // Called when a packet is received on this connection. + void OnReadPacket(const char* data, size_t size); + + // Called when a connection is determined to be no longer useful to us. We + // still keep it around in case the other side wants to use it. But we can + // safely stop pinging on it and we can allow it to time out if the other + // side stops using it as well. + bool pruned() { return pruned_; } + void Prune(); + + // Makes the connection go away. + void Destroy(); + + // Checks that the state of this connection is up-to-date. The argument is + // the current time, which is compared against various timeouts. + void UpdateState(uint32 now); + + // Called when this connection should try checking writability again. + uint32 last_ping_sent() { return last_ping_sent_; } + void Ping(uint32 now); + + // Called whenever a valid ping is received on this connection. This is + // public because the connection intercepts the first ping for us. + void ReceivedPing(); + + // Debugging description of this connection + std::string ToString() const; + + bool reported() { return reported_; } + void set_reported(bool reported) { reported_ = reported;} + +protected: + Port* port_; + size_t local_candidate_index_; + Candidate remote_candidate_; + ReadState read_state_; + WriteState write_state_; + bool connected_; + bool pruned_; + StunRequestManager requests_; + uint32 rtt_; + uint32 rtt_data_points_; + uint32 last_ping_sent_; // last time we sent a ping to the other side + uint32 last_ping_received_; // last time we received a ping from the other + // side + std::vector<uint32> pings_since_last_response_; + + size_t recv_total_bytes_; + size_t recv_bytes_second_; + uint32 last_recv_bytes_second_time_; + size_t last_recv_bytes_second_calc_; + + size_t sent_total_bytes_; + size_t sent_bytes_second_; + uint32 last_sent_bytes_second_time_; + size_t last_sent_bytes_second_calc_; + + // Callbacks from ConnectionRequest + void OnConnectionRequestResponse(StunMessage *response, uint32 rtt); + void OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt); + + // Called back when StunRequestManager has a stun packet to send + void OnSendStunPacket(const void* data, size_t size, StunRequest* req); + + // Constructs a new connection to the given remote port. + Connection(Port* port, size_t index, const Candidate& candidate); + + // Changes the state and signals if necessary. + void set_read_state(ReadState value); + void set_write_state(WriteState value); + void set_connected(bool value); + + // Checks if this connection is useless, and hence, should be destroyed. + void CheckTimeout(); + + void OnMessage(talk_base::Message *pmsg); + + friend class Port; + friend class ConnectionRequest; + +private: + bool reported_; +}; + +// ProxyConnection defers all the interesting work to the port + +class ProxyConnection : public Connection { +public: + ProxyConnection(Port* port, size_t index, const Candidate& candidate); + + virtual int Send(const void* data, size_t size); + virtual int GetError() { return error_; } + +private: + int error_; +}; + +} // namespace cricket + +#endif // __PORT_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc new file mode 100644 index 0000000..4687c19 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/port_unittest.cc @@ -0,0 +1,363 @@ +#include "talk/base/helpers.h" +#include "talk/base/host.h" +#include "talk/base/natserver.h" +#include "talk/base/natsocketfactory.h" +#include "talk/base/socketaddress.h" +#include "talk/base/thread.h" +#include "talk/base/virtualsocketserver.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/relayserver.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/stunserver.h" +#include <iostream> + +using namespace cricket; + +const uint32 MSG_CONNECT = 1; +const uint32 MSG_PREP_ADDRESS = 2; +const uint32 MSG_CREATE_CONN = 3; +const uint32 MSG_ACCEPT_CONN = 4; +const uint32 MSG_PING = 5; + +Candidate GetCandidate(Port* port) { + assert(port->candidates().size() == 1); + return port->candidates()[0]; +} + +talk_base::SocketAddress GetAddress(Port* port) { + return GetCandidate(port).address(); +} + +struct Foo : public talk_base::MessageHandler, public sigslot::has_slots<> { + int count; + talk_base::SocketAddress address; + StunMessage* request; + std::string remote_frag; + + talk_base::Thread* thread; + Port* port1; + Port* port2; + Connection* conn; + + Foo(talk_base::Thread* th, Port* p1, Port* p2) + : count(0), thread(th), port1(p1), port2(p2), conn(0) { + } + + void OnAddressReady(Port* port) { + count += 1; + } + + void OnUnknownAddress( + Port* port, const talk_base::SocketAddress& addr, StunMessage* msg, + const std::string& rf) { + assert(port == port1); + if (!address.IsAny()) { + assert(addr == address); + delete request; + } + address = addr; + request = msg; + remote_frag = rf; + } + + void OnMessage(talk_base::Message* pmsg) { + assert(talk_base::Thread::Current() == thread); + + switch (pmsg->message_id) { + case MSG_CONNECT: + port1->SignalAddressReady.connect(this, &Foo::OnAddressReady); + port1->SignalUnknownAddress.connect(this, &Foo::OnUnknownAddress); + break; + + case MSG_PREP_ADDRESS: + port1->PrepareAddress(); + break; + + case MSG_CREATE_CONN: + conn = port1->CreateConnection(GetCandidate(port2), Port::ORIGIN_MESSAGE); + assert(conn); + conn->Ping(0); + break; + + case MSG_PING: + assert(conn); + conn->Ping(0); + break; + + case MSG_ACCEPT_CONN: { + Candidate c = GetCandidate(port2); + c.set_address(address); + conn = port1->CreateConnection(c, Port::ORIGIN_MESSAGE); + assert(conn); + port1->SendBindingResponse(request, address); + conn->Ping(0); + delete request; + break; + } + + default: + assert(false); + break; + } + } +}; + +void test(talk_base::Thread* pthMain, const char* name1, Port* port1, + talk_base::Thread* pthBack, const char* name2, Port* port2, + bool accept = true, bool same_addr = true) { + Foo* foo1 = new Foo(pthMain, port1, port2); + Foo* foo2 = new Foo(pthBack, port2, port1); + + std::cout << "Test: " << name1 << " to " << name2 << ": "; + std::cout.flush(); + + pthBack->Start(); + + pthMain->Post(foo1, MSG_CONNECT); + pthBack->Post(foo2, MSG_CONNECT); + pthMain->ProcessMessages(10); + assert(foo1->count == 0); + assert(foo2->count == 0); + + pthMain->Post(foo1, MSG_PREP_ADDRESS); + pthMain->ProcessMessages(200); + assert(foo1->count == 1); + + pthBack->Post(foo2, MSG_PREP_ADDRESS); + pthMain->ProcessMessages(200); + assert(foo2->count == 1); + + pthMain->Post(foo1, MSG_CREATE_CONN); + pthMain->ProcessMessages(200); + + if (accept) { + + assert(foo1->address.IsAny()); + assert(foo2->remote_frag == port1->username_fragment()); + assert(!same_addr || (foo2->address == GetAddress(port1))); + + pthBack->Post(foo2, MSG_ACCEPT_CONN); + pthMain->ProcessMessages(200); + + } else { + + assert(foo1->address.IsAny()); + assert(foo2->address.IsAny()); + + pthBack->Post(foo2, MSG_CREATE_CONN); + pthMain->ProcessMessages(200); + + if (same_addr) { + assert(foo1->conn->read_state() == Connection::STATE_READABLE); + assert(foo2->conn->write_state() == Connection::STATE_WRITABLE); + + // First connection may not be writable if the first ping did not get + // through. So we will have to do another. + if (foo1->conn->write_state() == Connection::STATE_WRITE_CONNECT) { + pthMain->Post(foo1, MSG_PING); + pthMain->ProcessMessages(200); + } + } else { + assert(foo1->address.IsAny()); + assert(foo2->address.IsAny()); + + pthMain->Post(foo1, MSG_PING); + pthMain->ProcessMessages(200); + + assert(foo1->address.IsAny()); + assert(!foo2->address.IsAny()); + + pthBack->Post(foo2, MSG_ACCEPT_CONN); + pthMain->ProcessMessages(200); + } + } + + assert(foo1->conn->read_state() == Connection::STATE_READABLE); + assert(foo1->conn->write_state() == Connection::STATE_WRITABLE); + assert(foo2->conn->read_state() == Connection::STATE_READABLE); + assert(foo2->conn->write_state() == Connection::STATE_WRITABLE); + + pthBack->Stop(); + + delete port1; + delete port2; + delete foo1; + delete foo2; + + std::cout << "PASS" << std::endl; +} + +const talk_base::SocketAddress local_addr = talk_base::SocketAddress("127.0.0.1", 0); +const talk_base::SocketAddress relay_int_addr = talk_base::SocketAddress("127.0.0.1", 5000); +const talk_base::SocketAddress relay_ext_addr = talk_base::SocketAddress("127.0.0.1", 5001); +const talk_base::SocketAddress stun_addr = talk_base::SocketAddress("127.0.0.1", STUN_SERVER_PORT); +const talk_base::SocketAddress nat_addr = talk_base::SocketAddress("127.0.0.1", talk_base::NAT_SERVER_PORT); + +void test_udp() { + talk_base::Thread* pthMain = talk_base::Thread::Current(); + talk_base::Thread* pthBack = new talk_base::Thread(); + talk_base::Network* network = new talk_base::Network("network", local_addr.ip()); + + test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr), + pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr)); + + delete pthBack; +} + +void test_relay() { + talk_base::Thread* pthMain = talk_base::Thread::Current(); + talk_base::Thread* pthBack = new talk_base::Thread(); + talk_base::Network* network = new talk_base::Network("network", local_addr.ip()); + + RelayServer relay_server(pthBack); + + talk_base::AsyncUDPSocket* int_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver()); + assert(int_socket->Bind(relay_int_addr) >= 0); + relay_server.AddInternalSocket(int_socket); + + talk_base::AsyncUDPSocket* ext_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver()); + assert(ext_socket->Bind(relay_ext_addr) >= 0); + relay_server.AddExternalSocket(ext_socket); + + std::string username = CreateRandomString(16); + std::string password = CreateRandomString(16); + + RelayPort* rport = + new RelayPort(pthBack, NULL, network, local_addr, username, password, ""); + rport->AddServerAddress(ProtocolAddress(relay_int_addr, PROTO_UDP)); + + test(pthMain, "udp", new UDPPort(pthMain, NULL, network, local_addr), + pthBack, "relay", rport); + + delete pthBack; +} + +const char* NATName(talk_base::NATType type) { + switch (type) { + case talk_base::NAT_OPEN_CONE: return "open cone"; + case talk_base::NAT_ADDR_RESTRICTED: return "addr restricted"; + case talk_base::NAT_PORT_RESTRICTED: return "port restricted"; + case talk_base::NAT_SYMMETRIC: return "symmetric"; + default: + assert(false); + return 0; + } +} + +void test_stun(talk_base::NATType nat_type) { + talk_base::Thread* pthMain = talk_base::Thread::Current(); + talk_base::Thread* pthBack = new talk_base::Thread(); + talk_base::Network* network = new talk_base::Network("network", local_addr.ip()); + + talk_base::NATServer* nat = new talk_base::NATServer( + nat_type, pthMain->socketserver(), nat_addr, + pthMain->socketserver(), nat_addr); + talk_base::NATSocketFactory* nat_factory = + new talk_base::NATSocketFactory(pthMain->socketserver(), nat_addr); + + StunPort* stun_port = + new StunPort(pthMain, nat_factory, network, local_addr, stun_addr); + + talk_base::AsyncUDPSocket* stun_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); + assert(stun_socket->Bind(stun_addr) >= 0); + StunServer* stun_server = new StunServer(stun_socket); + + char name[256]; + sprintf(name, "stun (%s)", NATName(nat_type)); + + test(pthMain, name, stun_port, + pthBack, "udp", new UDPPort(pthBack, NULL, network, local_addr), + true, nat_type != talk_base::NAT_SYMMETRIC); + + delete stun_server; + delete stun_socket; + delete nat; + delete nat_factory; + delete pthBack; +} + +void test_stun(talk_base::NATType nat1_type, talk_base::NATType nat2_type) { + + talk_base::Thread* pthMain = talk_base::Thread::Current(); + talk_base::Thread* pthBack = new talk_base::Thread(); + talk_base::Network* network = new talk_base::Network("network", local_addr.ip()); + + talk_base::SocketAddress local_addr1(talk_base::LocalHost().networks()[0]->ip(), 0); + talk_base::SocketAddress local_addr2(talk_base::LocalHost().networks()[1]->ip(), 0); + + talk_base::SocketAddress nat1_addr(local_addr1.ip(), talk_base::NAT_SERVER_PORT); + talk_base::SocketAddress nat2_addr(local_addr2.ip(), talk_base::NAT_SERVER_PORT); + + talk_base::SocketAddress stun1_addr(local_addr1.ip(), STUN_SERVER_PORT); + talk_base::SocketAddress stun2_addr(local_addr2.ip(), STUN_SERVER_PORT); + + talk_base::NATServer* nat1 = new talk_base::NATServer( + nat1_type, pthMain->socketserver(), nat1_addr, + pthMain->socketserver(), nat1_addr); + talk_base::NATSocketFactory* nat1_factory = + new talk_base::NATSocketFactory(pthMain->socketserver(), nat1_addr); + + StunPort* stun1_port = + new StunPort(pthMain, nat1_factory, network, local_addr1, stun1_addr); + + talk_base::NATServer* nat2 = new talk_base::NATServer( + nat2_type, pthBack->socketserver(), nat2_addr, + pthBack->socketserver(), nat2_addr); + talk_base::NATSocketFactory* nat2_factory = + new talk_base::NATSocketFactory(pthBack->socketserver(), nat2_addr); + + StunPort* stun2_port = + new StunPort(pthMain, nat2_factory, network, local_addr2, stun2_addr); + + talk_base::AsyncUDPSocket* stun1_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); + assert(stun1_socket->Bind(stun_addr) >= 0); + StunServer* stun1_server = new StunServer(stun1_socket); + + talk_base::AsyncUDPSocket* stun2_socket = talk_base::CreateAsyncUDPSocket(pthBack->socketserver()); + assert(stun2_socket->Bind(stun2_addr) >= 0); + StunServer* stun2_server = new StunServer(stun2_socket); + + char name1[256], name2[256]; + sprintf(name1, "stun (%s)", NATName(nat1_type)); + sprintf(name2, "stun (%s)", NATName(nat2_type)); + + test(pthMain, name1, stun1_port, + pthBack, name2, stun2_port, + nat2_type == talk_base::NAT_OPEN_CONE, nat1_type != talk_base::NAT_SYMMETRIC); + + delete stun1_server; + delete stun1_socket; + delete stun2_server; + delete stun2_socket; + delete nat1; + delete nat1_factory; + delete nat2; + delete nat2_factory; + delete pthBack; +} + +int main(int argc, char* argv[]) { + InitRandom(NULL, 0); + + test_udp(); + + test_relay(); + + test_stun(talk_base::NAT_OPEN_CONE); + test_stun(talk_base::NAT_ADDR_RESTRICTED); + test_stun(talk_base::NAT_PORT_RESTRICTED); + test_stun(talk_base::NAT_SYMMETRIC); + + test_stun(talk_base::NAT_OPEN_CONE, talk_base::NAT_OPEN_CONE); + test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_OPEN_CONE); + test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_OPEN_CONE); + test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_OPEN_CONE); + + test_stun(talk_base::NAT_ADDR_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED); + test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_ADDR_RESTRICTED); + test_stun(talk_base::NAT_PORT_RESTRICTED, talk_base::NAT_PORT_RESTRICTED); + test_stun(talk_base::NAT_SYMMETRIC, talk_base::NAT_ADDR_RESTRICTED); + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h b/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h new file mode 100644 index 0000000..50f52cf --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/portallocator.h @@ -0,0 +1,104 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _PORTALLOCATOR_H_ +#define _PORTALLOCATOR_H_ + +#include "talk/base/sigslot.h" +#include "talk/p2p/base/port.h" +#include <string> +#undef SetPort + +namespace cricket { + +// PortAllocator is responsible for allocating Port types for a given +// P2PSocket. It also handles port freeing. +// +// Clients can override this class to control port allocation, including +// what kinds of ports are allocated. + +const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01; +const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02; +const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04; +const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08; +const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10; + +const uint32 kDefaultPortAllocatorFlags = 0; + +class PortAllocatorSession : public sigslot::has_slots<> { +public: + PortAllocatorSession(uint32 flags) : flags_(flags) {} + + // Subclasses should clean up any ports created. + virtual ~PortAllocatorSession() {} + + uint32 flags() const { return flags_; } + void set_flags(uint32 flags) { flags_ = flags; } + + // Prepares an initial set of ports to try. + virtual void GetInitialPorts() = 0; + + // Starts and stops the flow of additional ports to try. + virtual void StartGetAllPorts() = 0; + virtual void StopGetAllPorts() = 0; + virtual bool IsGettingAllPorts() = 0; + + sigslot::signal2<PortAllocatorSession*, Port*> SignalPortReady; + sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady; + + uint32 generation() { return generation_; } + void set_generation(uint32 generation) { generation_ = generation; } + +private: + uint32 flags_; + uint32 generation_; +}; + +class PortAllocator { +public: + PortAllocator() : flags_(kDefaultPortAllocatorFlags) {} + + virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) = 0; + + uint32 flags() const { return flags_; } + void set_flags(uint32 flags) { flags_ = flags; } + + const std::string& user_agent() const { return agent_; } + const talk_base::ProxyInfo& proxy() const { return proxy_; } + void set_proxy(const std::string& agent, const talk_base::ProxyInfo& proxy) { + agent_ = agent; proxy_ = proxy; + } + +protected: + uint32 flags_; + std::string agent_; + talk_base::ProxyInfo proxy_; +}; + +} // namespace cricket + +#endif // _PORTALLOCATOR_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc new file mode 100644 index 0000000..2f52d65 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.cc @@ -0,0 +1,1071 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/basicdefs.h" +#include "talk/base/basictypes.h" +#include "talk/base/byteorder.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/socket.h" +#include "talk/base/stringutils.h" +#include "talk/base/time.h" +#include "talk/p2p/base/pseudotcp.h" + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +// The following logging is for detailed (packet-level) pseudotcp analysis only. +#define _DBG_NONE 0 +#define _DBG_NORMAL 1 +#define _DBG_VERBOSE 2 +#define _DEBUGMSG _DBG_NONE + +namespace cricket { + +////////////////////////////////////////////////////////////////////// +// Network Constants +////////////////////////////////////////////////////////////////////// + +// Standard MTUs +const uint16 PACKET_MAXIMUMS[] = { + 65535, // Theoretical maximum, Hyperchannel + 32000, // Nothing + 17914, // 16Mb IBM Token Ring + 8166, // IEEE 802.4 + //4464, // IEEE 802.5 (4Mb max) + 4352, // FDDI + //2048, // Wideband Network + 2002, // IEEE 802.5 (4Mb recommended) + //1536, // Expermental Ethernet Networks + //1500, // Ethernet, Point-to-Point (default) + 1492, // IEEE 802.3 + 1006, // SLIP, ARPANET + //576, // X.25 Networks + //544, // DEC IP Portal + //512, // NETBIOS + 508, // IEEE 802/Source-Rt Bridge, ARCNET + 296, // Point-to-Point (low delay) + //68, // Official minimum + 0, // End of list marker +}; + +const uint32 MAX_PACKET = 65535; +// Note: we removed lowest level because packet overhead was larger! +const uint32 MIN_PACKET = 296; + +const uint32 IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?) +const uint32 ICMP_HEADER_SIZE = 8; +const uint32 UDP_HEADER_SIZE = 8; +// TODO: Make JINGLE_HEADER_SIZE transparent to this code? +const uint32 JINGLE_HEADER_SIZE = 64; // when relay framing is in use + +////////////////////////////////////////////////////////////////////// +// Global Constants and Functions +////////////////////////////////////////////////////////////////////// +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | Conversation Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 8 | Acknowledgment Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | |U|A|P|R|S|F| | +// 12 | Control | |R|C|S|S|Y|I| Window | +// | | |G|K|H|T|N|N| | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 16 | Timestamp sending | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 20 | Timestamp receiving | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 24 | data | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +////////////////////////////////////////////////////////////////////// + +#define PSEUDO_KEEPALIVE 0 + +const uint32 MAX_SEQ = 0xFFFFFFFF; +const uint32 HEADER_SIZE = 24; +const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE; + +const uint32 MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second") +const uint32 DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1) +const uint32 MAX_RTO = 60000; // 60 seconds +const uint32 ACK_DELAY = 100; // 100 milliseconds + +const uint8 FLAG_CTL = 0x02; +const uint8 FLAG_RST = 0x04; + +const uint8 CTL_CONNECT = 0; +//const uint8 CTL_REDIRECT = 1; +const uint8 CTL_EXTRA = 255; + +/* +const uint8 FLAG_FIN = 0x01; +const uint8 FLAG_SYN = 0x02; +const uint8 FLAG_ACK = 0x10; +*/ + +const uint32 CTRL_BOUND = 0x80000000; + +const long DEFAULT_TIMEOUT = 4000; // If there are no pending clocks, wake up every 4 seconds +const long CLOSED_TIMEOUT = 60 * 1000; // If the connection is closed, once per minute + +#if PSEUDO_KEEPALIVE +// !?! Rethink these times +const uint32 IDLE_PING = 20 * 1000; // 20 seconds (note: WinXP SP2 firewall udp timeout is 90 seconds) +const uint32 IDLE_TIMEOUT = 90 * 1000; // 90 seconds; +#endif // PSEUDO_KEEPALIVE + +////////////////////////////////////////////////////////////////////// +// Helper Functions +////////////////////////////////////////////////////////////////////// + +inline void long_to_bytes(uint32 val, void* buf) { + *static_cast<uint32*>(buf) = talk_base::HostToNetwork32(val); +} + +inline void short_to_bytes(uint16 val, void* buf) { + *static_cast<uint16*>(buf) = talk_base::HostToNetwork16(val); +} + +inline uint32 bytes_to_long(const void* buf) { + return talk_base::NetworkToHost32(*static_cast<const uint32*>(buf)); +} + +inline uint16 bytes_to_short(const void* buf) { + return talk_base::NetworkToHost16(*static_cast<const uint16*>(buf)); +} + +uint32 bound(uint32 lower, uint32 middle, uint32 upper) { + return talk_base::_min(talk_base::_max(lower, middle), upper); +} + +////////////////////////////////////////////////////////////////////// +// Debugging Statistics +////////////////////////////////////////////////////////////////////// + +#if 0 // Not used yet + +enum Stat { + S_SENT_PACKET, // All packet sends + S_RESENT_PACKET, // All packet sends that are retransmits + S_RECV_PACKET, // All packet receives + S_RECV_NEW, // All packet receives that are too new + S_RECV_OLD, // All packet receives that are too old + S_NUM_STATS +}; + +const char* const STAT_NAMES[S_NUM_STATS] = { + "snt", + "snt-r", + "rcv" + "rcv-n", + "rcv-o" +}; + +int g_stats[S_NUM_STATS]; +inline void Incr(Stat s) { ++g_stats[s]; } +void ReportStats() { + char buffer[256]; + size_t len = 0; + for (int i=0; i<S_NUM_STATS; ++i) { + len += talk_base::sprintfn(buffer, ARRAY_SIZE(buffer), "%s%s:%d", + (i == 0) ? "" : ",", STAT_NAMES[i], g_stats[i]); + g_stats[i] = 0; + } + LOG(LS_INFO) << "Stats[" << buffer << "]"; +} + +#endif + +////////////////////////////////////////////////////////////////////// +// PseudoTcp +////////////////////////////////////////////////////////////////////// + +uint32 PseudoTcp::Now() { +#if 0 // Use this to synchronize timers with logging timestamps (easier debug) + return talk_base::ElapsedTime(); +#else + return talk_base::Time(); +#endif +} + +PseudoTcp::PseudoTcp(IPseudoTcpNotify * notify, uint32 conv) + : m_notify(notify), m_shutdown(SD_NONE), m_error(0) { + + // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic) + ASSERT(sizeof(m_rbuf) + MIN_PACKET < sizeof(m_sbuf)); + + uint32 now = Now(); + + m_state = TCP_LISTEN; + m_conv = conv; + m_rcv_wnd = sizeof(m_rbuf); + m_snd_nxt = m_slen = 0; + m_snd_wnd = 1; + m_snd_una = m_rcv_nxt = m_rlen = 0; + m_bReadEnable = true; + m_bWriteEnable = false; + m_t_ack = 0; + + m_msslevel = 0; + m_largest = 0; + ASSERT(MIN_PACKET > PACKET_OVERHEAD); + m_mss = MIN_PACKET - PACKET_OVERHEAD; + m_mtu_advise = MAX_PACKET; + + m_rto_base = 0; + + m_cwnd = 2 * m_mss; + m_ssthresh = sizeof(m_rbuf); + m_lastrecv = m_lastsend = m_lasttraffic = now; + m_bOutgoing = false; + + m_dup_acks = 0; + m_recover = 0; + + m_ts_recent = m_ts_lastack = 0; + + m_rx_rto = DEF_RTO; + m_rx_srtt = m_rx_rttvar = 0; +} + +PseudoTcp::~PseudoTcp() { +} + +int +PseudoTcp::Connect() { + if (m_state != TCP_LISTEN) { + m_error = EINVAL; + return -1; + } + + m_state = TCP_SYN_SENT; + LOG(LS_INFO) << "State: TCP_SYN_SENT"; + + char buffer[1]; + buffer[0] = CTL_CONNECT; + queue(buffer, 1, true); + attemptSend(); + + return 0; +} + +void +PseudoTcp::NotifyMTU(uint16 mtu) { + m_mtu_advise = mtu; + if (m_state == TCP_ESTABLISHED) { + adjustMTU(); + } +} + +void +PseudoTcp::NotifyClock(uint32 now) { + if (m_state == TCP_CLOSED) + return; + + // Check if it's time to retransmit a segment + if (m_rto_base && (talk_base::TimeDiff(m_rto_base + m_rx_rto, now) <= 0)) { + if (m_slist.empty()) { + ASSERT(false); + } else { + // Note: (m_slist.front().xmit == 0)) { + // retransmit segments +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "timeout retransmit (rto: " << m_rx_rto + << ") (rto_base: " << m_rto_base + << ") (now: " << now + << ") (dup_acks: " << static_cast<unsigned>(m_dup_acks) + << ")"; +#endif // _DEBUGMSG + if (!transmit(m_slist.begin(), now)) { + closedown(ECONNABORTED); + return; + } + + uint32 nInFlight = m_snd_nxt - m_snd_una; + m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss); + //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss; + m_cwnd = m_mss; + + // Back off retransmit timer. Note: the limit is lower when connecting. + uint32 rto_limit = (m_state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO; + m_rx_rto = talk_base::_min(rto_limit, m_rx_rto * 2); + m_rto_base = now; + } + } + + // Check if it's time to probe closed windows + if ((m_snd_wnd == 0) + && (talk_base::TimeDiff(m_lastsend + m_rx_rto, now) <= 0)) { + if (talk_base::TimeDiff(now, m_lastrecv) >= 15000) { + closedown(ECONNABORTED); + return; + } + + // probe the window + packet(m_snd_nxt - 1, 0, 0, 0); + m_lastsend = now; + + // back off retransmit timer + m_rx_rto = talk_base::_min(MAX_RTO, m_rx_rto * 2); + } + + // Check if it's time to send delayed acks + if (m_t_ack && (talk_base::TimeDiff(m_t_ack + ACK_DELAY, now) <= 0)) { + packet(m_snd_nxt, 0, 0, 0); + } + +#if PSEUDO_KEEPALIVE + // Check for idle timeout + if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lastrecv + IDLE_TIMEOUT, now) <= 0)) { + closedown(ECONNABORTED); + return; + } + + // Check for ping timeout (to keep udp mapping open) + if ((m_state == TCP_ESTABLISHED) && (TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now) <= 0)) { + packet(m_snd_nxt, 0, 0, 0); + } +#endif // PSEUDO_KEEPALIVE +} + +bool +PseudoTcp::NotifyPacket(const char * buffer, size_t len) { + if (len > MAX_PACKET) { + LOG_F(WARNING) << "packet too large"; + return false; + } + return parse(reinterpret_cast<const uint8 *>(buffer), uint32(len)); +} + +bool +PseudoTcp::GetNextClock(uint32 now, long& timeout) { + return clock_check(now, timeout); +} + +// +// IPStream Implementation +// + +int +PseudoTcp::Recv(char * buffer, size_t len) { + if (m_state != TCP_ESTABLISHED) { + m_error = ENOTCONN; + return SOCKET_ERROR; + } + + if (m_rlen == 0) { + m_bReadEnable = true; + m_error = EWOULDBLOCK; + return SOCKET_ERROR; + } + + uint32 read = talk_base::_min(uint32(len), m_rlen); + memcpy(buffer, m_rbuf, read); + m_rlen -= read; + + // !?! until we create a circular buffer, we need to move all of the rest of the buffer up! + memmove(m_rbuf, m_rbuf + read, sizeof(m_rbuf) - read/*m_rlen*/); + + if ((sizeof(m_rbuf) - m_rlen - m_rcv_wnd) + >= talk_base::_min<uint32>(sizeof(m_rbuf) / 2, m_mss)) { + bool bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business + + m_rcv_wnd = sizeof(m_rbuf) - m_rlen; + + if (bWasClosed) { + attemptSend(sfImmediateAck); + } + } + + return read; +} + +int +PseudoTcp::Send(const char * buffer, size_t len) { + if (m_state != TCP_ESTABLISHED) { + m_error = ENOTCONN; + return SOCKET_ERROR; + } + + if (m_slen == sizeof(m_sbuf)) { + m_bWriteEnable = true; + m_error = EWOULDBLOCK; + return SOCKET_ERROR; + } + + int written = queue(buffer, uint32(len), false); + attemptSend(); + return written; +} + +void +PseudoTcp::Close(bool force) { + LOG_F(LS_VERBOSE) << "(" << (force ? "true" : "false") << ")"; + m_shutdown = force ? SD_FORCEFUL : SD_GRACEFUL; +} + +int PseudoTcp::GetError() { + return m_error; +} + +// +// Internal Implementation +// + +uint32 +PseudoTcp::queue(const char * data, uint32 len, bool bCtrl) { + if (len > sizeof(m_sbuf) - m_slen) { + ASSERT(!bCtrl); + len = sizeof(m_sbuf) - m_slen; + } + + // We can concatenate data if the last segment is the same type + // (control v. regular data), and has not been transmitted yet + if (!m_slist.empty() && (m_slist.back().bCtrl == bCtrl) && (m_slist.back().xmit == 0)) { + m_slist.back().len += len; + } else { + SSegment sseg(m_snd_una + m_slen, len, bCtrl); + m_slist.push_back(sseg); + } + + memcpy(m_sbuf + m_slen, data, len); + m_slen += len; + //LOG(LS_INFO) << "PseudoTcp::queue - m_slen = " << m_slen; + return len; +} + +IPseudoTcpNotify::WriteResult +PseudoTcp::packet(uint32 seq, uint8 flags, const char * data, uint32 len) { + ASSERT(HEADER_SIZE + len <= MAX_PACKET); + + uint32 now = Now(); + + uint8 buffer[MAX_PACKET]; + long_to_bytes(m_conv, buffer); + long_to_bytes(seq, buffer + 4); + long_to_bytes(m_rcv_nxt, buffer + 8); + buffer[12] = 0; + buffer[13] = flags; + short_to_bytes(uint16(m_rcv_wnd), buffer + 14); + + // Timestamp computations + long_to_bytes(now, buffer + 16); + long_to_bytes(m_ts_recent, buffer + 20); + m_ts_lastack = m_rcv_nxt; + + memcpy(buffer + HEADER_SIZE, data, len); + +#if _DEBUGMSG >= _DBG_VERBOSE + LOG(LS_INFO) << "<-- <CONV=" << m_conv + << "><FLG=" << static_cast<unsigned>(flags) + << "><SEQ=" << seq << ":" << seq + len + << "><ACK=" << m_rcv_nxt + << "><WND=" << m_rcv_wnd + << "><TS=" << (now % 10000) + << "><TSR=" << (m_ts_recent % 10000) + << "><LEN=" << len << ">"; +#endif // _DEBUGMSG + + IPseudoTcpNotify::WriteResult wres = m_notify->TcpWritePacket(this, reinterpret_cast<char *>(buffer), len + HEADER_SIZE); + // Note: When data is NULL, this is an ACK packet. We don't read the return value for those, + // and thus we won't retry. So go ahead and treat the packet as a success (basically simulate + // as if it were dropped), which will prevent our timers from being messed up. + if ((wres != IPseudoTcpNotify::WR_SUCCESS) && (NULL != data)) + return wres; + + m_t_ack = 0; + if (len > 0) { + m_lastsend = now; + } + m_lasttraffic = now; + m_bOutgoing = true; + + return IPseudoTcpNotify::WR_SUCCESS; +} + +bool +PseudoTcp::parse(const uint8 * buffer, uint32 size) { + if (size < 12) + return false; + + Segment seg; + seg.conv = bytes_to_long(buffer); + seg.seq = bytes_to_long(buffer + 4); + seg.ack = bytes_to_long(buffer + 8); + seg.flags = buffer[13]; + seg.wnd = bytes_to_short(buffer + 14); + + seg.tsval = bytes_to_long(buffer + 16); + seg.tsecr = bytes_to_long(buffer + 20); + + seg.data = reinterpret_cast<const char *>(buffer) + HEADER_SIZE; + seg.len = size - HEADER_SIZE; + +#if _DEBUGMSG >= _DBG_VERBOSE + LOG(LS_INFO) << "--> <CONV=" << seg.conv + << "><FLG=" << static_cast<unsigned>(seg.flags) + << "><SEQ=" << seg.seq << ":" << seg.seq + seg.len + << "><ACK=" << seg.ack + << "><WND=" << seg.wnd + << "><TS=" << (seg.tsval % 10000) + << "><TSR=" << (seg.tsecr % 10000) + << "><LEN=" << seg.len << ">"; +#endif // _DEBUGMSG + + return process(seg); +} + +bool +PseudoTcp::clock_check(uint32 now, long& nTimeout) { + if (m_shutdown == SD_FORCEFUL) + return false; + + if ((m_shutdown == SD_GRACEFUL) + && ((m_state != TCP_ESTABLISHED) + || ((m_slen == 0) && (m_t_ack == 0)))) { + return false; + } + + if (m_state == TCP_CLOSED) { + nTimeout = CLOSED_TIMEOUT; + return true; + } + + nTimeout = DEFAULT_TIMEOUT; + + if (m_t_ack) { + nTimeout = talk_base::_min(nTimeout, + talk_base::TimeDiff(m_t_ack + ACK_DELAY, now)); + } + if (m_rto_base) { + nTimeout = talk_base::_min(nTimeout, + talk_base::TimeDiff(m_rto_base + m_rx_rto, now)); + } + if (m_snd_wnd == 0) { + nTimeout = talk_base::_min(nTimeout, talk_base::TimeDiff(m_lastsend + m_rx_rto, now)); + } +#if PSEUDO_KEEPALIVE + if (m_state == TCP_ESTABLISHED) { + nTimeout = talk_base::_min(nTimeout, + talk_base::TimeDiff(m_lasttraffic + (m_bOutgoing ? IDLE_PING * 3/2 : IDLE_PING), now)); + } +#endif // PSEUDO_KEEPALIVE + return true; +} + +bool +PseudoTcp::process(Segment& seg) { + // If this is the wrong conversation, send a reset!?! (with the correct conversation?) + if (seg.conv != m_conv) { + //if ((seg.flags & FLAG_RST) == 0) { + // packet(tcb, seg.ack, 0, FLAG_RST, 0, 0); + //} + LOG_F(LS_ERROR) << "wrong conversation"; + return false; + } + + uint32 now = Now(); + m_lasttraffic = m_lastrecv = now; + m_bOutgoing = false; + + if (m_state == TCP_CLOSED) { + // !?! send reset? + LOG_F(LS_ERROR) << "closed"; + return false; + } + + // Check if this is a reset segment + if (seg.flags & FLAG_RST) { + closedown(ECONNRESET); + return false; + } + + // Check for control data + bool bConnect = false; + if (seg.flags & FLAG_CTL) { + if (seg.len == 0) { + LOG_F(LS_ERROR) << "Missing control code"; + return false; + } else if (seg.data[0] == CTL_CONNECT) { + bConnect = true; + if (m_state == TCP_LISTEN) { + m_state = TCP_SYN_RECEIVED; + LOG(LS_INFO) << "State: TCP_SYN_RECEIVED"; + //m_notify->associate(addr); + char buffer[1]; + buffer[0] = CTL_CONNECT; + queue(buffer, 1, true); + } else if (m_state == TCP_SYN_SENT) { + m_state = TCP_ESTABLISHED; + LOG(LS_INFO) << "State: TCP_ESTABLISHED"; + adjustMTU(); + if (m_notify) { + m_notify->OnTcpOpen(this); + } + //notify(evOpen); + } + } else { + LOG_F(LS_WARNING) << "Unknown control code: " << seg.data[0]; + return false; + } + } + + // Update timestamp + if ((seg.seq <= m_ts_lastack) && (m_ts_lastack < seg.seq + seg.len)) { + m_ts_recent = seg.tsval; + } + + // Check if this is a valuable ack + if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) { + // Calculate round-trip time + if (seg.tsecr) { + long rtt = talk_base::TimeDiff(now, seg.tsecr); + if (rtt >= 0) { + if (m_rx_srtt == 0) { + m_rx_srtt = rtt; + m_rx_rttvar = rtt / 2; + } else { + m_rx_rttvar = (3 * m_rx_rttvar + abs(long(rtt - m_rx_srtt))) / 4; + m_rx_srtt = (7 * m_rx_srtt + rtt) / 8; + } + m_rx_rto = bound(MIN_RTO, m_rx_srtt + talk_base::_max(1LU, 4 * m_rx_rttvar), MAX_RTO); +#if _DEBUGMSG >= _DBG_VERBOSE + LOG(LS_INFO) << "rtt: " << rtt + << " srtt: " << m_rx_srtt + << " rto: " << m_rx_rto; +#endif // _DEBUGMSG + } else { + ASSERT(false); + } + } + + m_snd_wnd = seg.wnd; + + uint32 nAcked = seg.ack - m_snd_una; + m_snd_una = seg.ack; + + m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now; + + m_slen -= nAcked; + memmove(m_sbuf, m_sbuf + nAcked, m_slen); + //LOG(LS_INFO) << "PseudoTcp::process - m_slen = " << m_slen; + + for (uint32 nFree = nAcked; nFree > 0; ) { + ASSERT(!m_slist.empty()); + if (nFree < m_slist.front().len) { + m_slist.front().len -= nFree; + nFree = 0; + } else { + if (m_slist.front().len > m_largest) { + m_largest = m_slist.front().len; + } + nFree -= m_slist.front().len; + m_slist.pop_front(); + } + } + + if (m_dup_acks >= 3) { + if (m_snd_una >= m_recover) { // NewReno + uint32 nInFlight = m_snd_nxt - m_snd_una; + m_cwnd = talk_base::_min(m_ssthresh, nInFlight + m_mss); // (Fast Retransmit) +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "exit recovery"; +#endif // _DEBUGMSG + m_dup_acks = 0; + } else { +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "recovery retransmit"; +#endif // _DEBUGMSG + if (!transmit(m_slist.begin(), now)) { + closedown(ECONNABORTED); + return false; + } + m_cwnd += m_mss - talk_base::_min(nAcked, m_cwnd); + } + } else { + m_dup_acks = 0; + // Slow start, congestion avoidance + if (m_cwnd < m_ssthresh) { + m_cwnd += m_mss; + } else { + m_cwnd += talk_base::_max(1LU, m_mss * m_mss / m_cwnd); + } + } + + // !?! A bit hacky + if ((m_state == TCP_SYN_RECEIVED) && !bConnect) { + m_state = TCP_ESTABLISHED; + LOG(LS_INFO) << "State: TCP_ESTABLISHED"; + adjustMTU(); + if (m_notify) { + m_notify->OnTcpOpen(this); + } + //notify(evOpen); + } + + // If we make room in the send queue, notify the user + // The goal it to make sure we always have at least enough data to fill the + // window. We'd like to notify the app when we are halfway to that point. + const uint32 kIdealRefillSize = (sizeof(m_sbuf) + sizeof(m_rbuf)) / 2; + if (m_bWriteEnable && (m_slen < kIdealRefillSize)) { + m_bWriteEnable = false; + if (m_notify) { + m_notify->OnTcpWriteable(this); + } + //notify(evWrite); + } + } else if (seg.ack == m_snd_una) { + // !?! Note, tcp says don't do this... but otherwise how does a closed window become open? + m_snd_wnd = seg.wnd; + + // Check duplicate acks + if (seg.len > 0) { + // it's a dup ack, but with a data payload, so don't modify m_dup_acks + } else if (m_snd_una != m_snd_nxt) { + m_dup_acks += 1; + if (m_dup_acks == 3) { // (Fast Retransmit) +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "enter recovery"; + LOG(LS_INFO) << "recovery retransmit"; +#endif // _DEBUGMSG + if (!transmit(m_slist.begin(), now)) { + closedown(ECONNABORTED); + return false; + } + m_recover = m_snd_nxt; + uint32 nInFlight = m_snd_nxt - m_snd_una; + m_ssthresh = talk_base::_max(nInFlight / 2, 2 * m_mss); + //LOG(LS_INFO) << "m_ssthresh: " << m_ssthresh << " nInFlight: " << nInFlight << " m_mss: " << m_mss; + m_cwnd = m_ssthresh + 3 * m_mss; + } else if (m_dup_acks > 3) { + m_cwnd += m_mss; + } + } else { + m_dup_acks = 0; + } + } + + // Conditions were acks must be sent: + // 1) Segment is too old (they missed an ACK) (immediately) + // 2) Segment is too new (we missed a segment) (immediately) + // 3) Segment has data (so we need to ACK!) (delayed) + // ... so the only time we don't need to ACK, is an empty segment that points to rcv_nxt! + + SendFlags sflags = sfNone; + if (seg.seq != m_rcv_nxt) { + sflags = sfImmediateAck; // (Fast Recovery) + } else if (seg.len != 0) { + sflags = sfDelayedAck; + } +#if _DEBUGMSG >= _DBG_NORMAL + if (sflags == sfImmediateAck) { + if (seg.seq > m_rcv_nxt) { + LOG_F(LS_INFO) << "too new"; + } else if (seg.seq + seg.len <= m_rcv_nxt) { + LOG_F(LS_INFO) << "too old"; + } + } +#endif // _DEBUGMSG + + // Adjust the incoming segment to fit our receive buffer + if (seg.seq < m_rcv_nxt) { + uint32 nAdjust = m_rcv_nxt - seg.seq; + if (nAdjust < seg.len) { + seg.seq += nAdjust; + seg.data += nAdjust; + seg.len -= nAdjust; + } else { + seg.len = 0; + } + } + if ((seg.seq + seg.len - m_rcv_nxt) > (sizeof(m_rbuf) - m_rlen)) { + uint32 nAdjust = seg.seq + seg.len - m_rcv_nxt - (sizeof(m_rbuf) - m_rlen); + if (nAdjust < seg.len) { + seg.len -= nAdjust; + } else { + seg.len = 0; + } + } + + bool bIgnoreData = (seg.flags & FLAG_CTL) || (m_shutdown != SD_NONE); + bool bNewData = false; + + if (seg.len > 0) { + if (bIgnoreData) { + if (seg.seq == m_rcv_nxt) { + m_rcv_nxt += seg.len; + } + } else { + uint32 nOffset = seg.seq - m_rcv_nxt; + memcpy(m_rbuf + m_rlen + nOffset, seg.data, seg.len); + if (seg.seq == m_rcv_nxt) { + m_rlen += seg.len; + m_rcv_nxt += seg.len; + m_rcv_wnd -= seg.len; + bNewData = true; + + RList::iterator it = m_rlist.begin(); + while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) { + if (it->seq + it->len > m_rcv_nxt) { + sflags = sfImmediateAck; // (Fast Recovery) + uint32 nAdjust = (it->seq + it->len) - m_rcv_nxt; +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "Recovered " << nAdjust << " bytes (" << m_rcv_nxt << " -> " << m_rcv_nxt + nAdjust << ")"; +#endif // _DEBUGMSG + m_rlen += nAdjust; + m_rcv_nxt += nAdjust; + m_rcv_wnd -= nAdjust; + } + it = m_rlist.erase(it); + } + } else { +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "Saving " << seg.len << " bytes (" << seg.seq << " -> " << seg.seq + seg.len << ")"; +#endif // _DEBUGMSG + RSegment rseg; + rseg.seq = seg.seq; + rseg.len = seg.len; + RList::iterator it = m_rlist.begin(); + while ((it != m_rlist.end()) && (it->seq < rseg.seq)) { + ++it; + } + m_rlist.insert(it, rseg); + } + } + } + + attemptSend(sflags); + + // If we have new data, notify the user + if (bNewData && m_bReadEnable) { + m_bReadEnable = false; + if (m_notify) { + m_notify->OnTcpReadable(this); + } + //notify(evRead); + } + + return true; +} + +bool +PseudoTcp::transmit(const SList::iterator& seg, uint32 now) { + if (seg->xmit >= ((m_state == TCP_ESTABLISHED) ? 15 : 30)) { + LOG_F(LS_VERBOSE) << "too many retransmits"; + return false; + } + + uint32 nTransmit = talk_base::_min(seg->len, m_mss); + + while (true) { + uint32 seq = seg->seq; + uint8 flags = (seg->bCtrl ? FLAG_CTL : 0); + const char * buffer = m_sbuf + (seg->seq - m_snd_una); + IPseudoTcpNotify::WriteResult wres = this->packet(seq, flags, buffer, nTransmit); + + if (wres == IPseudoTcpNotify::WR_SUCCESS) + break; + + if (wres == IPseudoTcpNotify::WR_FAIL) { + LOG_F(LS_VERBOSE) << "packet failed"; + return false; + } + + ASSERT(wres == IPseudoTcpNotify::WR_TOO_LARGE); + + while (true) { + if (PACKET_MAXIMUMS[m_msslevel + 1] == 0) { + LOG_F(LS_VERBOSE) << "MTU too small"; + return false; + } + // !?! We need to break up all outstanding and pending packets and then retransmit!?! + + m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD; + m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula + if (m_mss < nTransmit) { + nTransmit = m_mss; + break; + } + } +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes"; +#endif // _DEBUGMSG + } + + if (nTransmit < seg->len) { + LOG_F(LS_VERBOSE) << "mss reduced to " << m_mss; + + SSegment subseg(seg->seq + nTransmit, seg->len - nTransmit, seg->bCtrl); + //subseg.tstamp = seg->tstamp; + subseg.xmit = seg->xmit; + seg->len = nTransmit; + + SList::iterator next = seg; + m_slist.insert(++next, subseg); + } + + if (seg->xmit == 0) { + m_snd_nxt += seg->len; + } + seg->xmit += 1; + //seg->tstamp = now; + if (m_rto_base == 0) { + m_rto_base = now; + } + + return true; +} + +void +PseudoTcp::attemptSend(SendFlags sflags) { + uint32 now = Now(); + + if (talk_base::TimeDiff(now, m_lastsend) > static_cast<long>(m_rx_rto)) { + m_cwnd = m_mss; + } + +#if _DEBUGMSG + bool bFirst = true; + UNUSED(bFirst); +#endif // _DEBUGMSG + + while (true) { + uint32 cwnd = m_cwnd; + if ((m_dup_acks == 1) || (m_dup_acks == 2)) { // Limited Transmit + cwnd += m_dup_acks * m_mss; + } + uint32 nWindow = talk_base::_min(m_snd_wnd, cwnd); + uint32 nInFlight = m_snd_nxt - m_snd_una; + uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0; + + uint32 nAvailable = talk_base::_min(m_slen - nInFlight, m_mss); + + if (nAvailable > nUseable) { + if (nUseable * 4 < nWindow) { + // RFC 813 - avoid SWS + nAvailable = 0; + } else { + nAvailable = nUseable; + } + } + +#if _DEBUGMSG >= _DBG_VERBOSE + if (bFirst) { + bFirst = false; + LOG(LS_INFO) << "[cwnd: " << m_cwnd + << " nWindow: " << nWindow + << " nInFlight: " << nInFlight + << " nAvailable: " << nAvailable + << " nQueued: " << m_slen - nInFlight + << " nEmpty: " << sizeof(m_sbuf) - m_slen + << " ssthresh: " << m_ssthresh << "]"; + } +#endif // _DEBUGMSG + + if (nAvailable == 0) { + if (sflags == sfNone) + return; + + // If this is an immediate ack, or the second delayed ack + if ((sflags == sfImmediateAck) || m_t_ack) { + packet(m_snd_nxt, 0, 0, 0); + } else { + m_t_ack = Now(); + } + return; + } + + // Nagle algorithm + if ((m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) { + return; + } + + // Find the next segment to transmit + SList::iterator it = m_slist.begin(); + while (it->xmit > 0) { + ++it; + ASSERT(it != m_slist.end()); + } + SList::iterator seg = it; + + // If the segment is too large, break it into two + if (seg->len > nAvailable) { + SSegment subseg(seg->seq + nAvailable, seg->len - nAvailable, seg->bCtrl); + seg->len = nAvailable; + m_slist.insert(++it, subseg); + } + + if (!transmit(seg, now)) { + LOG_F(LS_VERBOSE) << "transmit failed"; + // TODO: consider closing socket + return; + } + + sflags = sfNone; + } +} + +void +PseudoTcp::closedown(uint32 err) { + m_slen = 0; + + LOG(LS_INFO) << "State: TCP_CLOSED"; + m_state = TCP_CLOSED; + if (m_notify) { + m_notify->OnTcpClosed(this, err); + } + //notify(evClose, err); +} + +void +PseudoTcp::adjustMTU() { + // Determine our current mss level, so that we can adjust appropriately later + for (m_msslevel = 0; PACKET_MAXIMUMS[m_msslevel + 1] > 0; ++m_msslevel) { + if (static_cast<uint16>(PACKET_MAXIMUMS[m_msslevel]) <= m_mtu_advise) { + break; + } + } + m_mss = m_mtu_advise - PACKET_OVERHEAD; + // !?! Should we reset m_largest here? +#if _DEBUGMSG >= _DBG_NORMAL + LOG(LS_INFO) << "Adjusting mss to " << m_mss << " bytes"; +#endif // _DEBUGMSG + // Enforce minimums on ssthresh and cwnd + m_ssthresh = talk_base::_max(m_ssthresh, 2 * m_mss); + m_cwnd = talk_base::_max(m_cwnd, m_mss); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h new file mode 100644 index 0000000..cce23e1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/pseudotcp.h @@ -0,0 +1,182 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __PSEUDOTCP_H__ +#define __PSEUDOTCP_H__ + +#include <list> +#include "talk/base/basictypes.h" + +namespace cricket { + +////////////////////////////////////////////////////////////////////// +// IPseudoTcpNotify +////////////////////////////////////////////////////////////////////// + +class PseudoTcp; + +class IPseudoTcpNotify { +public: + // Notification of tcp events + virtual void OnTcpOpen(PseudoTcp * tcp) = 0; + virtual void OnTcpReadable(PseudoTcp * tcp) = 0; + virtual void OnTcpWriteable(PseudoTcp * tcp) = 0; + virtual void OnTcpClosed(PseudoTcp * tcp, uint32 nError) = 0; + + // Write the packet onto the network + enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL }; + virtual WriteResult TcpWritePacket(PseudoTcp * tcp, const char * buffer, size_t len) = 0; +}; + +////////////////////////////////////////////////////////////////////// +// PseudoTcp +////////////////////////////////////////////////////////////////////// + +class PseudoTcp { +public: + static uint32 Now(); + + PseudoTcp(IPseudoTcpNotify * notify, uint32 conv); + virtual ~PseudoTcp(); + + int Connect(); + int Recv(char * buffer, size_t len); + int Send(const char * buffer, size_t len); + void Close(bool force); + int GetError(); + + enum TcpState { TCP_LISTEN, TCP_SYN_SENT, TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSED }; + TcpState State() const { return m_state; } + + // Call this when the PMTU changes. + void NotifyMTU(uint16 mtu); + + // Call this based on timeout value returned from GetNextClock. + // It's ok to call this too frequently. + void NotifyClock(uint32 now); + + // Call this whenever a packet arrives. + // Returns true if the packet was processed successfully. + bool NotifyPacket(const char * buffer, size_t len); + + // Call this to determine the next time NotifyClock should be called. + // Returns false if the socket is ready to be destroyed. + bool GetNextClock(uint32 now, long& timeout); + +protected: + enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck }; + enum { + // Note: can't go as high as 1024 * 64, because of uint16 precision + kRcvBufSize = 1024 * 60, + // Note: send buffer should be larger to make sure we can always fill the + // receiver window + kSndBufSize = 1024 * 90 + }; + + struct Segment { + uint32 conv, seq, ack; + uint8 flags; + uint16 wnd; + const char * data; + uint32 len; + uint32 tsval, tsecr; + }; + + struct SSegment { + uint32 seq, len; + //uint32 tstamp; + uint8 xmit; + bool bCtrl; + + SSegment(uint32 s, uint32 l, bool c) : seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) { } + }; + typedef std::list<SSegment> SList; + + struct RSegment { + uint32 seq, len; + }; + + uint32 queue(const char * data, uint32 len, bool bCtrl); + + IPseudoTcpNotify::WriteResult packet(uint32 seq, uint8 flags, const char * data, uint32 len); + bool parse(const uint8 * buffer, uint32 size); + + void attemptSend(SendFlags sflags = sfNone); + + void closedown(uint32 err = 0); + + bool clock_check(uint32 now, long& nTimeout); + + bool process(Segment& seg); + bool transmit(const SList::iterator& seg, uint32 now); + + void adjustMTU(); + +private: + IPseudoTcpNotify * m_notify; + enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown; + int m_error; + + // TCB data + TcpState m_state; + uint32 m_conv; + bool m_bReadEnable, m_bWriteEnable, m_bOutgoing; + uint32 m_lasttraffic; + + // Incoming data + typedef std::list<RSegment> RList; + RList m_rlist; + char m_rbuf[kRcvBufSize]; + uint32 m_rcv_nxt, m_rcv_wnd, m_rlen, m_lastrecv; + + // Outgoing data + SList m_slist; + char m_sbuf[kSndBufSize]; + uint32 m_snd_nxt, m_snd_wnd, m_slen, m_lastsend, m_snd_una; + // Maximum segment size, estimated protocol level, largest segment sent + uint32 m_mss, m_msslevel, m_largest, m_mtu_advise; + // Retransmit timer + uint32 m_rto_base; + + // Timestamp tracking + uint32 m_ts_recent, m_ts_lastack; + + // Round-trip calculation + uint32 m_rx_rttvar, m_rx_srtt, m_rx_rto; + + // Congestion avoidance, Fast retransmit/recovery, Delayed ACKs + uint32 m_ssthresh, m_cwnd; + uint8 m_dup_acks; + uint32 m_recover; + uint32 m_t_ack; +}; + +////////////////////////////////////////////////////////////////////// + +} // namespace cricket + +#endif // __PSEUDOTCP_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc new file mode 100644 index 0000000..f2e230b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.cc @@ -0,0 +1,150 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/rawtransport.h" +#include "talk/base/common.h" +#include "talk/p2p/base/constants.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/rawtransportchannel.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace cricket { + +const std::string kNsRawTransport("http://www.google.com/transport/raw-udp"); +const buzz::QName kQnRawTransport(true, kNsRawTransport, "transport"); +const buzz::QName kQnRawChannel(true, kNsRawTransport, "channel"); +const buzz::QName kQnRawBehindSymmetricNat(true, buzz::STR_EMPTY, + "behind-symmetric-nat"); +const buzz::QName kQnRawCanReceiveFromSymmetricNat(true, buzz::STR_EMPTY, + "can-receive-from-symmetric-nat"); + +RawTransport::RawTransport(SessionManager* session_manager) + : Transport(session_manager, kNsRawTransport) { +} + +RawTransport::~RawTransport() { + DestroyAllChannels(); +} + +buzz::XmlElement* RawTransport::CreateTransportOffer() { + buzz::XmlElement* xml = new buzz::XmlElement(kQnRawTransport, true); + + // Assume that we are behind a symmetric NAT. Also note that we can't + // handle the adjustment necessary to talk to someone else who is behind + // a symmetric NAT. + xml->AddAttr(kQnRawBehindSymmetricNat, "true"); + xml->AddAttr(kQnRawCanReceiveFromSymmetricNat, "false"); + + return xml; +} + +buzz::XmlElement* RawTransport::CreateTransportAnswer() { + return new buzz::XmlElement(kQnRawTransport, true); +} + +bool RawTransport::OnTransportOffer(const buzz::XmlElement* elem) { + ASSERT(elem->Name() == kQnRawTransport); + + // If the other side is behind a symmetric NAT then we can't talk to him. + // We also bail if this attribute isn't specified. + if (!elem->HasAttr(kQnRawBehindSymmetricNat) + || elem->Attr(kQnRawBehindSymmetricNat) != "false") { + return false; + } + + // If the other side doesn't explicitly state that he can receive from + // someone behind a symmetric NAT, we bail. + if (!elem->HasAttr(kQnRawCanReceiveFromSymmetricNat) + || elem->Attr(kQnRawCanReceiveFromSymmetricNat) != "true") { + return false; + } + + // We don't support any options, so we ignore them. + return true; +} + +bool RawTransport::OnTransportAnswer(const buzz::XmlElement* elem) { + ASSERT(elem->Name() == kQnRawTransport); + // We don't support any options. We fail if any are given. The other side + // should know from our request that we expected an empty response. + return elem->FirstChild() == NULL; +} + +bool RawTransport::OnTransportMessage(const buzz::XmlElement* msg, + const buzz::XmlElement* stanza) { + ASSERT(msg->Name() == kQnRawTransport); + for (const buzz::XmlElement* elem = msg->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name() == kQnRawChannel) { + talk_base::SocketAddress addr; + if (!ParseAddress(stanza, elem, &addr)) + return false; + + ForwardChannelMessage(elem->Attr(buzz::QN_NAME), + new buzz::XmlElement(*elem)); + } + } + return true; +} + +bool RawTransport::OnTransportError(const buzz::XmlElement* session_msg, + const buzz::XmlElement* error) { + return true; +} + +bool RawTransport::ParseAddress(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + talk_base::SocketAddress* addr) { + // Make sure the required attributes exist + if (!elem->HasAttr(buzz::QN_NAME) || + !elem->HasAttr(QN_ADDRESS) || + !elem->HasAttr(QN_PORT)) { + return BadRequest(stanza, "channel missing required attribute", NULL); + } + + // Make sure the channel named actually exists. + if (!HasChannel(elem->Attr(buzz::QN_NAME))) + return BadRequest(stanza, "channel named does not exist", NULL); + + // Parse the address. + return Transport::ParseAddress(stanza, elem, addr); +} + +TransportChannelImpl* RawTransport::CreateTransportChannel( + const std::string& name, const std::string &session_type) { + return new RawTransportChannel( + name, session_type, this, session_manager()->port_allocator()); +} + +void RawTransport::DestroyTransportChannel(TransportChannelImpl* channel) { + delete channel; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h new file mode 100644 index 0000000..d273b40 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransport.h @@ -0,0 +1,84 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_RAWTRANSPORT_H_ +#define _CRICKET_P2P_BASE_RAWTRANSPORT_H_ + +#include "talk/p2p/base/transport.h" + +namespace cricket { + +// Xml names used to name this transport and create our elements +extern const std::string kNsRawTransport; +extern const buzz::QName kQnRawTransport; +extern const buzz::QName kQnRawChannel; +extern const buzz::QName kQnRawNatType; +extern const buzz::QName kQnRawNatTypeAllowed; + +// Implements a transport that only sends raw packets, no STUN. As a result, +// it cannot do pings to determine connectivity, so it only uses a single port +// that it thinks will work. +class RawTransport: public Transport { + public: + RawTransport(SessionManager* session_manager); + virtual ~RawTransport(); + + // Handles the raw transport protocol descriptions, which are trivial. + virtual buzz::XmlElement* CreateTransportOffer(); + virtual buzz::XmlElement* CreateTransportAnswer(); + virtual bool OnTransportOffer(const buzz::XmlElement* elem); + virtual bool OnTransportAnswer(const buzz::XmlElement* elem); + + // Forwards messages containing channel addresses to the appropriate channel. + virtual bool OnTransportMessage(const buzz::XmlElement* msg, + const buzz::XmlElement* stanza); + virtual bool OnTransportError(const buzz::XmlElement* session_msg, + const buzz::XmlElement* error); + + protected: + // Creates and destroys raw channels. + virtual TransportChannelImpl* CreateTransportChannel( + const std::string& name, const std::string &session_type); + virtual void DestroyTransportChannel(TransportChannelImpl* channel); + + private: + // Parses the given element, which should describe the address to use for a + // given channel. This will return false and signal an error if the address + // or channel name is bad. + bool ParseAddress(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + talk_base::SocketAddress* addr); + + friend class RawTransportChannel; // For ParseAddress. + + DISALLOW_EVIL_CONSTRUCTORS(RawTransport); +}; + +} // namespace cricket + + +#endif // _CRICKET_P2P_BASE_RAWTRANSPORT_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc new file mode 100644 index 0000000..13c1886 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.cc @@ -0,0 +1,259 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/rawtransportchannel.h" +#include "talk/base/common.h" +#include "talk/p2p/base/constants.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/rawtransport.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/stunport.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace { + +const int MSG_DESTROY_UNUSED_PORTS = 1; + +} // namespace + +namespace cricket { + +RawTransportChannel::RawTransportChannel( + const std::string &name, const std::string &session_type, RawTransport* transport, + PortAllocator *allocator) + : TransportChannelImpl(name, session_type), raw_transport_(transport), + allocator_(allocator), allocator_session_(NULL), stun_port_(NULL), + relay_port_(NULL), port_(NULL), use_relay_(false) { +} + +RawTransportChannel::~RawTransportChannel() { + delete allocator_session_; +} + +int RawTransportChannel::SendPacket(const char *data, size_t size) { + if (port_ == NULL) + return -1; + if (remote_address_.IsAny()) + return -1; + return port_->SendTo(data, size, remote_address_, true); +} + +int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { + // TODO: allow these to be set before we have a port + if (port_ == NULL) + return -1; + return port_->SetOption(opt, value); +} + +int RawTransportChannel::GetError() { + return (port_ != NULL) ? port_->GetError() : 0; +} + +void RawTransportChannel::Connect() { + // Create an allocator that only returns stun and relay ports. + allocator_session_ = allocator_->CreateSession(name(), session_type()); + + uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP; + +#if !defined(FEATURE_ENABLE_STUN_CLASSIFICATION) + flags |= PORTALLOCATOR_DISABLE_RELAY; +#endif + allocator_session_->set_flags(flags); + allocator_session_->SignalPortReady.connect( + this, &RawTransportChannel::OnPortReady); + allocator_session_->SignalCandidatesReady.connect( + this, &RawTransportChannel::OnCandidatesReady); + + // The initial ports will include stun. + allocator_session_->GetInitialPorts(); +} + +void RawTransportChannel::Reset() { + set_readable(false); + set_writable(false); + + delete allocator_session_; + + allocator_session_ = NULL; + stun_port_ = NULL; + relay_port_ = NULL; + port_ = NULL; + remote_address_ = talk_base::SocketAddress(); +} + +void RawTransportChannel::OnChannelMessage(const buzz::XmlElement* msg) { + bool valid = raw_transport_->ParseAddress(NULL, msg, &remote_address_); + ASSERT(valid); + ASSERT(!remote_address_.IsAny()); + set_readable(true); + + // We can write once we have a port and a remote address. + if (port_ != NULL) + SetWritable(); +} + +// Note about stun classification +// Code to classify our NAT type and use the relay port if we are behind an +// asymmetric NAT is under a FEATURE_ENABLE_STUN_CLASSIFICATION #define. +// To turn this one we will have to enable a second stun address and make sure +// that the relay server works for raw UDP. +// +// Another option is to classify the NAT type early and not offer the raw +// transport type at all if we can't support it. + +void RawTransportChannel::OnPortReady( + PortAllocatorSession* session, Port* port) { + ASSERT(session == allocator_session_); + + if (port->type() == STUN_PORT_TYPE) { + stun_port_ = static_cast<StunPort*>(port); + +#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION) + // We need a secondary address to determine the NAT type. + stun_port_->PrepareSecondaryAddress(); +#endif + } else if (port->type() == RELAY_PORT_TYPE) { + relay_port_ = static_cast<RelayPort*>(port); + } else { + ASSERT(false); + } +} + +void RawTransportChannel::OnCandidatesReady( + PortAllocatorSession *session, const std::vector<Candidate>& candidates) { + ASSERT(session == allocator_session_); + ASSERT(candidates.size() >= 1); + + // The most recent candidate is the one we haven't seen yet. + Candidate c = candidates[candidates.size() - 1]; + + if (c.type() == STUN_PORT_TYPE) { + ASSERT(stun_port_ != NULL); + +#if defined(FEATURE_ENABLE_STUN_CLASSIFICATION) + // We need to wait until we have two addresses. + if (stun_port_->candidates().size() < 2) + return; + + // This is the second address. If these addresses are the same, then we + // are not behind a symmetric NAT. Hence, a stun port should be sufficient. + if (stun_port_->candidates()[0].address() == + stun_port_->candidates()[1].address()) { + SetPort(stun_port_); + return; + } + + // We will need to use relay. + use_relay_ = true; + + // If we weren't given a relay port, we'll need to request it. + if (relay_port_ == NULL) { + allocator_session_->StartGetAllPorts(); + return; + } + + // If we already have a relay address, we're good. Otherwise, we will need + // to wait until one arrives. + if (relay_port_->candidates().size() > 0) + SetPort(relay_port_); +#else // defined(FEATURE_ENABLE_STUN_CLASSIFICATION) + // Always use the stun port. We don't classify right now so just assume it + // will work fine. + SetPort(stun_port_); +#endif + } else if (c.type() == RELAY_PORT_TYPE) { + if (use_relay_) + SetPort(relay_port_); + } else { + ASSERT(false); + } +} + +void RawTransportChannel::SetPort(Port* port) { + ASSERT(port_ == NULL); + port_ = port; + + // We don't need any ports other than the one we picked. + allocator_session_->StopGetAllPorts(); + raw_transport_->session_manager()->worker_thread()->Post( + this, MSG_DESTROY_UNUSED_PORTS, NULL); + + // Send a message to the other client containing our address. + + ASSERT(port_->candidates().size() >= 1); + ASSERT(port_->candidates()[0].protocol() == "udp"); + talk_base::SocketAddress addr = port_->candidates()[0].address(); + + buzz::XmlElement* msg = new buzz::XmlElement(kQnRawChannel); + msg->SetAttr(buzz::QN_NAME, name()); + msg->SetAttr(QN_ADDRESS, addr.IPAsString()); + msg->SetAttr(QN_PORT, addr.PortAsString()); + SignalChannelMessage(this, msg); + + // Read all packets from this port. + port_->EnablePortPackets(); + port_->SignalReadPacket.connect(this, &RawTransportChannel::OnReadPacket); + + // We can write once we have a port and a remote address. + if (!remote_address_.IsAny()) + SetWritable(); +} + +void RawTransportChannel::SetWritable() { + ASSERT(port_ != NULL); + ASSERT(!remote_address_.IsAny()); + + set_writable(true); + + SignalRouteChange(this, remote_address_); +} + +void RawTransportChannel::OnReadPacket( + Port* port, const char* data, size_t size, + const talk_base::SocketAddress& addr) { + ASSERT(port_ == port); + SignalReadPacket(this, data, size); +} + +void RawTransportChannel::OnMessage(talk_base::Message* msg) { + ASSERT(msg->message_id == MSG_DESTROY_UNUSED_PORTS); + ASSERT(port_ != NULL); + if (port_ != stun_port_) { + stun_port_->Destroy(); + stun_port_ = NULL; + } + if (port_ != relay_port_ && relay_port_ != NULL) { + relay_port_->Destroy(); + relay_port_ = NULL; + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h new file mode 100644 index 0000000..9dcbae2 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/rawtransportchannel.h @@ -0,0 +1,117 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_ +#define _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_ + +#include "talk/base/messagequeue.h" +#include "talk/p2p/base/transportchannelimpl.h" +#include "talk/p2p/base/rawtransport.h" +#include "talk/p2p/base/candidate.h" + +namespace cricket { + +class Port; +class Connection; +class StunPort; +class RelayPort; +class PortAllocator; +class PortAllocatorSession; + +// Implements a channel that just sends bare packets once we have received the +// address of the other side. We pick a single address to send them based on +// a simple investigation of NAT type. +class RawTransportChannel : public TransportChannelImpl, + public talk_base::MessageHandler { + public: + RawTransportChannel(const std::string &name, + const std::string &session_type, + RawTransport* transport, + PortAllocator *allocator); + virtual ~RawTransportChannel(); + + // Implementation of normal channel packet sending. + virtual int SendPacket(const char *data, size_t len); + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + + // Returns the raw transport that created this channel. + virtual Transport* GetTransport() { return raw_transport_; } + + // Creates an allocator session to start figuring out which type of port we + // should send to the other client. This will send SignalChannelMessage once + // we have decided. + virtual void Connect(); + + // Resets state back to unconnected. + virtual void Reset(); + + // We don't actually worry about signaling since we can't send new candidates. + virtual void OnSignalingReady() {} + + // Handles a message setting the remote address. We are writable once we + // have this since we now know where to send. + virtual void OnChannelMessage(const buzz::XmlElement* msg); + + private: + RawTransport* raw_transport_; + PortAllocator* allocator_; + PortAllocatorSession* allocator_session_; + StunPort* stun_port_; + RelayPort* relay_port_; + Port* port_; + bool use_relay_; + talk_base::SocketAddress remote_address_; + + // Called when the allocator creates another port. + void OnPortReady(PortAllocatorSession* session, Port* port); + + // Called when one of the ports we are using has determined its address. + void OnCandidatesReady(PortAllocatorSession *session, + const std::vector<Candidate>& candidates); + + // Called once we have chosen the port to use for communication with the + // other client. This will send its address and prepare the port for use. + void SetPort(Port* port); + + // Called once we have a port and a remote address. This will set mark the + // channel as writable and signal the route to the client. + void SetWritable(); + + // Called when we receive a packet from the other client. + void OnReadPacket(Port* port, const char* data, size_t size, + const talk_base::SocketAddress& addr); + + // Handles a message to destroy unused ports. + virtual void OnMessage(talk_base::Message *msg); + + DISALLOW_EVIL_CONSTRUCTORS(RawTransportChannel); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_RAWTRANSPORTCHANNEL_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc new file mode 100644 index 0000000..a8a0527 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/relayport.cc @@ -0,0 +1,634 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/asynctcpsocket.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/relayport.h" +#include <iostream> +#include <cassert> +#ifdef OSX +#include <errno.h> +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace talk_base { +class AsyncTCPSocket; +}; + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 60 * 1000; +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +// Manages a single connection to the relayserver. We aim to use each +// connection for only a specific destination address so that we can avoid +// wrapping every packet in a STUN send / data indication. +class RelayEntry : public sigslot::has_slots<> { +public: + RelayEntry(RelayPort* port, const talk_base::SocketAddress& ext_addr, + const talk_base::SocketAddress& local_addr); + ~RelayEntry(); + + RelayPort* port() { return port_; } + + const talk_base::SocketAddress& address() const { return ext_addr_; } + void set_address(const talk_base::SocketAddress& addr) { ext_addr_ = addr; } + + talk_base::AsyncPacketSocket* socket() { return socket_; } + + bool connected() const { return connected_; } + bool locked() const { return locked_; } + + // Returns the last error on the socket of this entry. + int GetError() const { return socket_->GetError(); } + + // Sends the STUN requests to the server to initiate this connection. + void Connect(); + + // Called when this entry becomes connected. The address given is the one + // exposed to the outside world on the relay server. + void OnConnect(const talk_base::SocketAddress& mapped_addr); + + // Sends a packet to the given destination address using the socket of this + // entry. This will wrap the packet in STUN if necessary. + int SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr); + + // Schedules a keep-alive allocate request. + void ScheduleKeepAlive(); + + void SetServerIndex(size_t sindex) { server_index_ = sindex; } + size_t ServerIndex() const { return server_index_; } + + // Try a different server address + void HandleConnectFailure(); + +private: + RelayPort* port_; + talk_base::SocketAddress ext_addr_, local_addr_; + size_t server_index_; + talk_base::AsyncPacketSocket* socket_; + bool connected_; + bool locked_; + StunRequestManager requests_; + + // Called when a TCP connection is established or fails + void OnSocketConnect(talk_base::AsyncTCPSocket* socket); + void OnSocketClose(talk_base::AsyncTCPSocket* socket, int error); + + // Called when a packet is received on this socket. + void OnReadPacket( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + + // Called on behalf of a StunRequest to write data to the socket. This is + // already STUN intended for the server, so no wrapping is necessary. + void OnSendPacket(const void* data, size_t size, StunRequest* req); + + // Sends the given data on the socket to the server with no wrapping. This + // returns the number of bytes written or -1 if an error occurred. + int SendPacket(const void* data, size_t size); +}; + +// Handles an allocate request for a particular RelayEntry. +class AllocateRequest : public StunRequest { +public: + AllocateRequest(RelayEntry* entry); + virtual ~AllocateRequest() {} + + virtual void Prepare(StunMessage* request); + + virtual int GetNextDelay(); + + virtual void OnResponse(StunMessage* response); + virtual void OnErrorResponse(StunMessage* response); + virtual void OnTimeout(); + +private: + RelayEntry* entry_; + uint32 start_time_; +}; + +const std::string RELAY_PORT_TYPE("relay"); + +RelayPort::RelayPort( + talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, const talk_base::SocketAddress& local_addr, + const std::string& username, const std::string& password, + const std::string& magic_cookie) + : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr), + ready_(false), magic_cookie_(magic_cookie), error_(0) { + + entries_.push_back( + new RelayEntry(this, talk_base::SocketAddress(), local_addr_)); + + set_username_fragment(username); + set_password(password); + + if (magic_cookie_.size() == 0) + magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4); +} + +RelayPort::~RelayPort() { + for (unsigned i = 0; i < entries_.size(); ++i) + delete entries_[i]; + thread_->Clear(this); +} + +void RelayPort::AddServerAddress(const ProtocolAddress& addr) { + // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP + if ((addr.proto == PROTO_SSLTCP) + && ((proxy().type == talk_base::PROXY_HTTPS) + || (proxy().type == talk_base::PROXY_UNKNOWN))) { + server_addr_.push_front(addr); + } else { + server_addr_.push_back(addr); + } +} + +void RelayPort::AddExternalAddress(const ProtocolAddress& addr) { + std::string proto_name = ProtoToString(addr.proto); + for (std::vector<Candidate>::const_iterator it = candidates().begin(); it != candidates().end(); ++it) { + if ((it->address() == addr.address) && (it->protocol() == proto_name)) { + LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString(); + return; + } + } + AddAddress(addr.address, proto_name, false); +} + +void RelayPort::SetReady() { + if (!ready_) { + ready_ = true; + SignalAddressReady(this); + } +} + +const ProtocolAddress * RelayPort::ServerAddress(size_t index) const { + if ((index >= 0) && (index < server_addr_.size())) + return &server_addr_[index]; + return 0; +} + +bool RelayPort::HasMagicCookie(const char* data, size_t size) { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp(data + 24, + magic_cookie_.c_str(), + magic_cookie_.size()); + } +} + +void RelayPort::PrepareAddress() { + // We initiate a connect on the first entry. If this completes, it will fill + // in the server address as the address of this port. + assert(entries_.size() == 1); + entries_[0]->Connect(); + ready_ = false; +} + +Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) { + // We only create connections to non-udp sockets if they are incoming on this port + if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT)) + return 0; + + // We don't support loopback on relays + if (address.type() == type()) + return 0; + + size_t index = 0; + for (size_t i = 0; i < candidates().size(); ++i) { + const Candidate& local = candidates()[i]; + if (local.protocol() == address.protocol()) { + index = i; + break; + } + } + + Connection * conn = new ProxyConnection(this, index, address); + AddConnection(conn); + return conn; +} + +int RelayPort::SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload) { + + // Try to find an entry for this specific address. Note that the first entry + // created was not given an address initially, so it can be set to the first + // address that comes along. + + RelayEntry* entry = 0; + + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->address().IsAny() && payload) { + entry = entries_[i]; + entry->set_address(addr); + break; + } else if (entries_[i]->address() == addr) { + entry = entries_[i]; + break; + } + } + + // If we did not find one, then we make a new one. This will not be useable + // until it becomes connected, however. + if (!entry && payload) { + entry = new RelayEntry(this, addr, local_addr_); + if (!entries_.empty()) { + // Use the same port to connect to relay server + entry->SetServerIndex(entries_[0]->ServerIndex()); + } + entry->Connect(); + entries_.push_back(entry); + } + + // If the entry is connected, then we can send on it (though wrapping may + // still be necessary). Otherwise, we can't yet use this connection, so we + // default to the first one. + if (!entry || !entry->connected()) { + assert(!entries_.empty()); + entry = entries_[0]; + if (!entry->connected()) { + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + } + + // Send the actual contents to the server using the usual mechanism. + int sent = entry->SendTo(data, size, addr); + if (sent <= 0) { + assert(sent < 0); + error_ = entry->GetError(); + return SOCKET_ERROR; + } + + // The caller of the function is expecting the number of user data bytes, + // rather than the size of the packet. + return (int)size; +} + +int RelayPort::SetOption(talk_base::Socket::Option opt, int value) { + int result = 0; + for (unsigned i = 0; i < entries_.size(); ++i) { + if (entries_[i]->socket()->SetOption(opt, value) < 0) { + result = -1; + error_ = entries_[i]->socket()->GetError(); + } + } + options_.push_back(OptionValue(opt, value)); + return result; +} + +int RelayPort::GetError() { + return error_; +} + +void RelayPort::OnReadPacket( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +void RelayPort::DisposeSocket(talk_base::AsyncPacketSocket * socket) { + thread_->Dispose(socket); +} + +RelayEntry::RelayEntry(RelayPort* port, + const talk_base::SocketAddress& ext_addr, + const talk_base::SocketAddress& local_addr) + : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0), + socket_(0), connected_(false), locked_(false), requests_(port->thread()) { + + requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket); +} + +RelayEntry::~RelayEntry() { + delete socket_; +} + +void RelayEntry::Connect() { + assert(socket_ == 0); + const ProtocolAddress * ra = port()->ServerAddress(server_index_); + if (!ra) { + LOG(INFO) << "Out of relay server connections"; + return; + } + + LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString(); + + socket_ = port_->CreatePacketSocket(ra->proto); + assert(socket_ != 0); + + socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket); + if (socket_->Bind(local_addr_) < 0) + LOG(INFO) << "bind: " << std::strerror(socket_->GetError()); + + for (unsigned i = 0; i < port_->options().size(); ++i) + socket_->SetOption(port_->options()[i].first, port_->options()[i].second); + + if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) { + talk_base::AsyncTCPSocket * tcp + = static_cast<talk_base::AsyncTCPSocket *>(socket_); + tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose); + tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); + tcp->Connect(ra->address); + } else { + requests_.Send(new AllocateRequest(this)); + } +} + +void RelayEntry::OnConnect(const talk_base::SocketAddress& mapped_addr) { + ProtocolType proto = PROTO_UDP; + LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString(); + connected_ = true; + + port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); + port_->SetReady(); +} + +int RelayEntry::SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr) { + + // If this connection is locked to the address given, then we can send the + // packet with no wrapper. + if (locked_ && (ext_addr_ == addr)) + return SendPacket(data, size); + + // Otherwise, we must wrap the given data in a STUN SEND request so that we + // can communicate the destination address to the server. + // + // Note that we do not use a StunRequest here. This is because there is + // likely no reason to resend this packet. If it is late, we just drop it. + // The next send to this address will try again. + + StunMessage request; + request.SetType(STUN_SEND_REQUEST); + request.SetTransactionID(CreateRandomString(16)); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(), + (uint16)port_->magic_cookie().size()); + request.AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes(port_->username_fragment().c_str(), + (uint16)port_->username_fragment().size()); + request.AddAttribute(username_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(addr.ip()); + addr_attr->SetPort(addr.port()); + request.AddAttribute(addr_attr); + + // Attempt to lock + if (ext_addr_ == addr) { + StunUInt32Attribute* options_attr = + StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS); + options_attr->SetValue(0x1); + request.AddAttribute(options_attr); + } + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + data_attr->CopyBytes(data, (uint16)size); + request.AddAttribute(data_attr); + + // TODO: compute the HMAC. + + talk_base::ByteBuffer buf; + request.Write(&buf); + + return SendPacket(buf.Data(), buf.Length()); +} + +void RelayEntry::ScheduleKeepAlive() { + requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY); +} + +void RelayEntry::HandleConnectFailure() { + //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT) + // return; + //ScheduleKeepAlive(); + + connected_ = false; + port()->DisposeSocket(socket_); + socket_ = 0; + requests_.Clear(); + + server_index_ += 1; + Connect(); +} + +void RelayEntry::OnSocketConnect(talk_base::AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString(); + requests_.Send(new AllocateRequest(this)); +} + +void RelayEntry::OnSocketClose(talk_base::AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + PLOG(LERROR, error) << "relay tcp connect failed"; + HandleConnectFailure(); +} + +void RelayEntry::OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + assert(socket == socket_); + //assert(remote_addr == port_->server_addr()); TODO: are we worried about this? + + // If the magic cookie is not present, then this is an unwrapped packet sent + // by the server, The actual remote address is the one we recorded. + if (!port_->HasMagicCookie(data, size)) { + if (locked_) { + port_->OnReadPacket(data, size, ext_addr_); + } else { + LOG(WARNING) << "Dropping packet: entry not locked"; + } + return; + } + + talk_base::ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) { + LOG(INFO) << "Incoming packet was not STUN"; + return; + } + + // The incoming packet should be a STUN ALLOCATE response, SEND response, or + // DATA indication. + if (requests_.CheckResponse(&msg)) { + return; + } else if (msg.type() == STUN_SEND_RESPONSE) { + if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) { + if (options_attr->value() & 0x1) { + locked_ = true; + } + } + return; + } else if (msg.type() != STUN_DATA_INDICATION) { + LOG(INFO) << "Received BAD stun type from server: " << msg.type() + ; + return; + } + + // This must be a data indication. + + const StunAddressAttribute* addr_attr = + msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2); + if (!addr_attr) { + LOG(INFO) << "Data indication has no source address"; + return; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Source address has bad family"; + return; + } + + talk_base::SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port()); + + const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + LOG(INFO) << "Data indication has no data"; + return; + } + + // Process the actual data and remote address in the normal manner. + port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2); +} + +void RelayEntry::OnSendPacket(const void* data, size_t size, StunRequest* req) { + SendPacket(data, size); +} + +int RelayEntry::SendPacket(const void* data, size_t size) { + const ProtocolAddress * ra = port_->ServerAddress(server_index_); + if (!ra) { + if (socket_) + socket_->SetError(ENOTCONN); + return SOCKET_ERROR; + } + int sent = socket_->SendTo(data, size, ra->address); + if (sent <= 0) { + LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError()); + assert(sent < 0); + } + return sent; +} + +AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) { + start_time_ = talk_base::GetMillisecondCount(); +} + +void AllocateRequest::Prepare(StunMessage* request) { + request->SetType(STUN_ALLOCATE_REQUEST); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes( + entry_->port()->magic_cookie().c_str(), + (uint16)entry_->port()->magic_cookie().size()); + request->AddAttribute(magic_cookie_attr); + + StunByteStringAttribute* username_attr = + StunAttribute::CreateByteString(STUN_ATTR_USERNAME); + username_attr->CopyBytes( + entry_->port()->username_fragment().c_str(), + (uint16)entry_->port()->username_fragment().size()); + request->AddAttribute(username_attr); +} + +int AllocateRequest::GetNextDelay() { + int delay = 100 * talk_base::_max(1 << count_, 2); + count_ += 1; + if (count_ == 5) + timeout_ = true; + return delay; +} + +void AllocateRequest::OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(INFO) << "Allocate response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(INFO) << "Mapped address has bad family"; + } else { + talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port()); + entry_->OnConnect(addr); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(INFO) << "Bad allocate response error code"; + } else { + LOG(INFO) << "Allocate error response:" + << " code=" << static_cast<int>(attr->error_code()) + << " reason='" << attr->reason() << "'"; + } + + if (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT) + entry_->ScheduleKeepAlive(); +} + +void AllocateRequest::OnTimeout() { + LOG(INFO) << "Allocate request timed out"; + entry_->HandleConnectFailure(); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayport.h b/Plugins/jingle/libjingle/talk/p2p/base/relayport.h new file mode 100644 index 0000000..5691a6c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/relayport.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __RELAYPORT_H__ +#define __RELAYPORT_H__ + +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/stunrequest.h" +#include <vector> + +namespace cricket { + +extern const std::string RELAY_PORT_TYPE; +class RelayEntry; + +// Communicates using an allocated port on the relay server. +class RelayPort : public Port { +public: + RelayPort( + talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network*, const talk_base::SocketAddress& local_addr, + const std::string& username, const std::string& password, + const std::string& magic_cookie); + virtual ~RelayPort(); + + void AddServerAddress(const ProtocolAddress& addr); + void AddExternalAddress(const ProtocolAddress& addr); + + typedef std::pair<talk_base::Socket::Option, int> OptionValue; + const std::vector<OptionValue>& options() const { return options_; } + + const std::string& magic_cookie() const { return magic_cookie_; } + bool HasMagicCookie(const char* data, size_t size); + + virtual void PrepareAddress(); + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + + const ProtocolAddress * ServerAddress(size_t index) const; + + void DisposeSocket(talk_base::AsyncPacketSocket * socket); + +protected: + void SetReady(); + + virtual int SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload); + + // Dispatches the given packet to the port or connection as appropriate. + void OnReadPacket( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); + +private: + friend class RelayEntry; + + talk_base::SocketAddress local_addr_; + std::deque<ProtocolAddress> server_addr_; + bool ready_; + std::vector<RelayEntry*> entries_; + std::vector<OptionValue> options_; + std::string magic_cookie_; + int error_; +}; + +} // namespace cricket + +#endif // __RELAYPORT_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc new file mode 100644 index 0000000..1f4a979 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.cc @@ -0,0 +1,671 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/relayserver.h" +#include "talk/base/helpers.h" +#include <algorithm> +#include <cassert> +#include <cstring> +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +// By default, we require a ping every 90 seconds. +const int MAX_LIFETIME = 15 * 60 * 1000; + +// The number of bytes in each of the usernames we use. +const uint32 USERNAME_LENGTH = 16; + +// Calls SendTo on the given socket and logs any bad results. +void Send(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, + const talk_base::SocketAddress& addr) { + int result = socket->SendTo(bytes, size, addr); + if (result < int(size)) { + std::cerr << "SendTo wrote only " << result << " of " << int(size) + << " bytes" << std::endl; + } else if (result < 0) { + std::cerr << "SendTo: " << std::strerror(errno) << std::endl; + } +} + +// Sends the given STUN message on the given socket. +void SendStun(const StunMessage& msg, + talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& addr) { + talk_base::ByteBuffer buf; + msg.Write(&buf); + Send(socket, buf.Data(), buf.Length(), addr); +} + +// Constructs a STUN error response and sends it on the given socket. +void SendStunError(const StunMessage& msg, talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& remote_addr, int error_code, + const char* error_desc, const std::string& magic_cookie) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + if (magic_cookie.size() == 0) + magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4); + else + magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size()); + err_msg.AddAttribute(magic_cookie_attr); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendStun(err_msg, socket, remote_addr); +} + +RelayServer::RelayServer(talk_base::Thread* thread) + : thread_(thread), log_bindings_(true) { +} + +RelayServer::~RelayServer() { + for (unsigned i = 0; i < internal_sockets_.size(); i++) + delete internal_sockets_[i]; + for (unsigned i = 0; i < external_sockets_.size(); i++) + delete external_sockets_[i]; +} + +void RelayServer::AddInternalSocket(talk_base::AsyncPacketSocket* socket) { + assert(internal_sockets_.end() == + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket)); + internal_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket); +} + +void RelayServer::RemoveInternalSocket(talk_base::AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(internal_sockets_.begin(), internal_sockets_.end(), socket); + assert(iter != internal_sockets_.end()); + internal_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::AddExternalSocket(talk_base::AsyncPacketSocket* socket) { + assert(external_sockets_.end() == + std::find(external_sockets_.begin(), external_sockets_.end(), socket)); + external_sockets_.push_back(socket); + socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket); +} + +void RelayServer::RemoveExternalSocket(talk_base::AsyncPacketSocket* socket) { + SocketList::iterator iter = + std::find(external_sockets_.begin(), external_sockets_.end(), socket); + assert(iter != external_sockets_.end()); + external_sockets_.erase(iter); + socket->SignalReadPacket.disconnect(this); +} + +void RelayServer::OnInternalPacket( + const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this did not come from an existing connection, it should be a STUN + // allocate request. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter == connections_.end()) { + HandleStunAllocate(bytes, size, ap, socket); + return; + } + + RelayServerConnection* int_conn = piter->second; + + // Handle STUN requests to the server itself. + if (int_conn->binding()->HasMagicCookie(bytes, size)) { + HandleStun(int_conn, bytes, size); + return; + } + + // Otherwise, this is a non-wrapped packet that we are to forward. Make sure + // that this connection has been locked. (Otherwise, we would not know what + // address to forward to.) + if (!int_conn->locked()) { + std::cerr << "Dropping packet: connection not locked" << std::endl; + return; + } + + // Forward this to the destination address into the connection. + RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection( + int_conn->default_destination()); + if (ext_conn && ext_conn->locked()) { + // TODO: Check the HMAC. + ext_conn->Send(bytes, size); + } else { + // This happens very often and is not an error. + //std::cerr << "Dropping packet: no external connection" << std::endl; + } +} + +void RelayServer::OnExternalPacket( + const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + + // Get the address of the connection we just received on. + talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + assert(!ap.destination().IsAny()); + + // If this connection already exists, then forward the traffic. + ConnectionMap::iterator piter = connections_.find(ap); + if (piter != connections_.end()) { + // TODO: Check the HMAC. + RelayServerConnection* ext_conn = piter->second; + RelayServerConnection* int_conn = + ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); + ext_conn->Lock(); // allow outgoing packets + return; + } + + // The first packet should always be a STUN / TURN packet. If it isn't, then + // we should just ignore this packet. + StunMessage msg; + talk_base::ByteBuffer buf = talk_base::ByteBuffer(bytes, size); + if (!msg.Read(&buf)) { + std::cerr << "Dropping packet: first packet not STUN" << std::endl; + return; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg.GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + std::cerr << "Dropping packet: no username" << std::endl; + return; + } + + uint32 length = talk_base::_min(uint32(username_attr->length()), USERNAME_LENGTH); + std::string username(username_attr->bytes(), length); + // TODO: Check the HMAC. + + // The binding should already be present. + BindingMap::iterator biter = bindings_.find(username); + if (biter == bindings_.end()) { + // TODO: Turn this back on. This is the sign of a client bug. + //std::cerr << "Dropping packet: no binding with username" << std::endl; + return; + } + + // Add this authenticted connection to the binding. + RelayServerConnection* ext_conn = + new RelayServerConnection(biter->second, ap, socket); + ext_conn->binding()->AddExternalConnection(ext_conn); + AddConnection(ext_conn); + + // We always know where external packets should be forwarded, so we can lock + // them from the beginning. + ext_conn->Lock(); + + // Send this message on the appropriate internal connection. + RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection( + ext_conn->addr_pair().source()); + assert(int_conn); + int_conn->Send(bytes, size, ext_conn->addr_pair().source()); +} + +bool RelayServer::HandleStun( + const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket, std::string* username, StunMessage* msg) { + + // Parse this into a stun message. + talk_base::ByteBuffer buf = talk_base::ByteBuffer(bytes, size); + if (!msg->Read(&buf)) { + SendStunError(*msg, socket, remote_addr, 400, "Bad Request", ""); + return false; + } + + // The initial packet should have a username (which identifies the binding). + const StunByteStringAttribute* username_attr = + msg->GetByteString(STUN_ATTR_USERNAME); + if (!username_attr) { + SendStunError(*msg, socket, remote_addr, 432, "Missing Username", ""); + return false; + } + + // Record the username if requested. + if (username) + username->append(username_attr->bytes(), username_attr->length()); + + // TODO: Check for unknown attributes (<= 0x7fff) + + return true; +} + +void RelayServer::HandleStunAllocate( + const char* bytes, size_t size, const talk_base::SocketAddressPair& ap, + talk_base::AsyncPacketSocket* socket) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, ap.source(), socket, &username, &request)) + return; + + // Make sure this is a an allocate request. + if (request.type() != STUN_ALLOCATE_REQUEST) { + SendStunError(request, + socket, + ap.source(), + 600, + "Operation Not Supported", + ""); + return; + } + + // TODO: Check the HMAC. + + // Find or create the binding for this username. + + RelayServerBinding* binding; + + BindingMap::iterator biter = bindings_.find(username); + if (biter != bindings_.end()) { + + binding = biter->second; + + } else { + + // NOTE: In the future, bindings will be created by the bot only. This + // else-branch will then disappear. + + // Compute the appropriate lifetime for this binding. + uint32 lifetime = MAX_LIFETIME; + const StunUInt32Attribute* lifetime_attr = + request.GetUInt32(STUN_ATTR_LIFETIME); + if (lifetime_attr) + lifetime = talk_base::_min(lifetime, lifetime_attr->value() * 1000); + + binding = new RelayServerBinding(this, username, "0", lifetime); + binding->SignalTimeout.connect(this, &RelayServer::OnTimeout); + bindings_[username] = binding; + + if (log_bindings_) { + std::cout << "Added new binding: " << bindings_.size() << " total" + << std::endl; + } + } + + // Add this connection to the binding. It starts out unlocked. + RelayServerConnection* int_conn = + new RelayServerConnection(binding, ap, socket); + binding->AddInternalConnection(int_conn); + AddConnection(int_conn); + + // Now that we have a connection, this other method takes over. + HandleStunAllocate(int_conn, request); +} + +void RelayServer::HandleStun( + RelayServerConnection* int_conn, const char* bytes, size_t size) { + + // Make sure this is a valid STUN request. + StunMessage request; + std::string username; + if (!HandleStun(bytes, size, int_conn->addr_pair().source(), + int_conn->socket(), &username, &request)) + return; + + // Make sure the username is the one were were expecting. + if (username != int_conn->binding()->username()) { + int_conn->SendStunError(request, 430, "Stale Credentials"); + return; + } + + // TODO: Check the HMAC. + + // Send this request to the appropriate handler. + if (request.type() == STUN_SEND_REQUEST) + HandleStunSend(int_conn, request); + else if (request.type() == STUN_ALLOCATE_REQUEST) + HandleStunAllocate(int_conn, request); + else + int_conn->SendStunError(request, 600, "Operation Not Supported"); +} + +void RelayServer::HandleStunAllocate( + RelayServerConnection* int_conn, const StunMessage& request) { + + // Create a response message that includes an address with which external + // clients can communicate. + + StunMessage response; + response.SetType(STUN_ALLOCATE_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + size_t index = rand() % external_sockets_.size(); + talk_base::SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress(); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + addr_attr->SetFamily(1); + addr_attr->SetIP(ext_addr.ip()); + addr_attr->SetPort(ext_addr.port()); + response.AddAttribute(addr_attr); + + StunUInt32Attribute* res_lifetime_attr = + StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME); + res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000); + response.AddAttribute(res_lifetime_attr); + + // TODO: Support transport-prefs (preallocate RTCP port). + // TODO: Support bandwidth restrictions. + // TODO: Add message integrity check. + + // Send a response to the caller. + int_conn->SendStun(response); +} + +void RelayServer::HandleStunSend( + RelayServerConnection* int_conn, const StunMessage& request) { + + const StunAddressAttribute* addr_attr = + request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS); + if (!addr_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + const StunByteStringAttribute* data_attr = + request.GetByteString(STUN_ATTR_DATA); + if (!data_attr) { + int_conn->SendStunError(request, 400, "Bad Request"); + return; + } + + talk_base::SocketAddress ext_addr(addr_attr->ip(), addr_attr->port()); + RelayServerConnection* ext_conn = + int_conn->binding()->GetExternalConnection(ext_addr); + if (!ext_conn) { + // Create a new connection to establish the relationship with this binding. + assert(external_sockets_.size() == 1); + talk_base::AsyncPacketSocket* socket = external_sockets_[0]; + talk_base::SocketAddressPair ap(ext_addr, socket->GetLocalAddress()); + ext_conn = new RelayServerConnection(int_conn->binding(), ap, socket); + ext_conn->binding()->AddExternalConnection(ext_conn); + AddConnection(ext_conn); + } + + // If this connection has pinged us, then allow outgoing traffic. + if (ext_conn->locked()) + ext_conn->Send(data_attr->bytes(), data_attr->length()); + + const StunUInt32Attribute* options_attr = + request.GetUInt32(STUN_ATTR_OPTIONS); + if (options_attr && (options_attr->value() & 0x01 != 0)) { + int_conn->set_default_destination(ext_addr); + int_conn->Lock(); + + StunMessage response; + response.SetType(STUN_SEND_RESPONSE); + response.SetTransactionID(request.transaction_id()); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(), + int_conn->binding()->magic_cookie().size()); + response.AddAttribute(magic_cookie_attr); + + StunUInt32Attribute* options2_attr = + StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS); + options2_attr->SetValue(0x01); + response.AddAttribute(options2_attr); + + int_conn->SendStun(response); + } +} + +void RelayServer::AddConnection(RelayServerConnection* conn) { + assert(connections_.find(conn->addr_pair()) == connections_.end()); + connections_[conn->addr_pair()] = conn; +} + +void RelayServer::RemoveConnection(RelayServerConnection* conn) { + ConnectionMap::iterator iter = connections_.find(conn->addr_pair()); + assert(iter != connections_.end()); + connections_.erase(iter); +} + +void RelayServer::RemoveBinding(RelayServerBinding* binding) { + BindingMap::iterator iter = bindings_.find(binding->username()); + assert(iter != bindings_.end()); + bindings_.erase(iter); + + if (log_bindings_) { + std::cout << "Removed a binding: " << bindings_.size() << " remaining" + << std::endl; + } +} + +void RelayServer::OnTimeout(RelayServerBinding* binding) { + // This call will result in all of the necessary clean-up. + delete binding; +} + +RelayServerConnection::RelayServerConnection( + RelayServerBinding* binding, const talk_base::SocketAddressPair& addrs, + talk_base::AsyncPacketSocket* socket) + : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) { + + // The creation of a new connection constitutes a use of the binding. + binding_->NoteUsed(); +} + +RelayServerConnection::~RelayServerConnection() { + // Remove this connection from the server's map (if it exists there). + binding_->server()->RemoveConnection(this); +} + +void RelayServerConnection::Send(const char* data, size_t size) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::Send(socket_, data, size, addr_pair_.source()); +} + +void RelayServerConnection::Send( + const char* data, size_t size, const talk_base::SocketAddress& from_addr) { + // If the from address is known to the client, we don't need to send it. + if (locked() && (from_addr == default_dest_)) { + Send(data, size); + return; + } + + // Wrap the given data in a data-indication packet. + + StunMessage msg; + msg.SetType(STUN_DATA_INDICATION); + msg.SetTransactionID("0000000000000000"); + + StunByteStringAttribute* magic_cookie_attr = + StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE); + magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(), + binding_->magic_cookie().size()); + msg.AddAttribute(magic_cookie_attr); + + StunAddressAttribute* addr_attr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2); + addr_attr->SetFamily(1); + addr_attr->SetIP(from_addr.ip()); + addr_attr->SetPort(from_addr.port()); + msg.AddAttribute(addr_attr); + + StunByteStringAttribute* data_attr = + StunAttribute::CreateByteString(STUN_ATTR_DATA); + assert(size <= 65536); + data_attr->CopyBytes(data, uint16(size)); + msg.AddAttribute(data_attr); + + SendStun(msg); +} + +void RelayServerConnection::SendStun(const StunMessage& msg) { + // Note that the binding has been used again. + binding_->NoteUsed(); + + cricket::SendStun(msg, socket_, addr_pair_.source()); +} + +void RelayServerConnection::SendStunError( + const StunMessage& request, int error_code, const char* error_desc) { + // An error does not indicate use. If no legitimate use off the binding + // occurs, we want it to be cleaned up even if errors are still occuring. + + cricket::SendStunError( + request, socket_, addr_pair_.source(), error_code, error_desc, + binding_->magic_cookie()); +} + +void RelayServerConnection::Lock() { + locked_ = true; +} + +void RelayServerConnection::Unlock() { + locked_ = false; +} + +// IDs used for posted messages: +const uint32 MSG_LIFETIME_TIMER = 1; + +RelayServerBinding::RelayServerBinding( + RelayServer* server, const std::string& username, + const std::string& password, uint32 lifetime) + : server_(server), username_(username), password_(password), + lifetime_(lifetime) { + + // For now, every connection uses the standard magic cookie value. + magic_cookie_.append( + reinterpret_cast<const char*>(STUN_MAGIC_COOKIE_VALUE), 4); + + // Initialize the last-used time to now. + NoteUsed(); + + // Set the first timeout check. + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); +} + +RelayServerBinding::~RelayServerBinding() { + // Clear the outstanding timeout check. + server_->thread()->Clear(this); + + // Clean up all of the connections. + for (size_t i = 0; i < internal_connections_.size(); ++i) + delete internal_connections_[i]; + for (size_t i = 0; i < external_connections_.size(); ++i) + delete external_connections_[i]; + + // Remove this binding from the server's map. + server_->RemoveBinding(this); +} + +void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) { + internal_connections_.push_back(conn); +} + +void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) { + external_connections_.push_back(conn); +} + +void RelayServerBinding::NoteUsed() { + last_used_ = talk_base::Time(); +} + +bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const { + if (size < 24 + magic_cookie_.size()) { + return false; + } else { + return 0 == std::memcmp( + bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()); + } +} + +RelayServerConnection* RelayServerBinding::GetInternalConnection( + const talk_base::SocketAddress& ext_addr) { + + // Look for an internal connection that is locked to this address. + for (size_t i = 0; i < internal_connections_.size(); ++i) { + if (internal_connections_[i]->locked() && + (ext_addr == internal_connections_[i]->default_destination())) + return internal_connections_[i]; + } + + // If one was not found, we send to the first connection. + assert(internal_connections_.size() > 0); + return internal_connections_[0]; +} + +RelayServerConnection* RelayServerBinding::GetExternalConnection( + const talk_base::SocketAddress& ext_addr) { + for (size_t i = 0; i < external_connections_.size(); ++i) { + if (ext_addr == external_connections_[i]->addr_pair().source()) + return external_connections_[i]; + } + return 0; +} + +void RelayServerBinding::OnMessage(talk_base::Message *pmsg) { + if (pmsg->message_id == MSG_LIFETIME_TIMER) { + assert(!pmsg->pdata); + + // If the lifetime timeout has been exceeded, then send a signal. + // Otherwise, just keep waiting. + if (talk_base::Time() >= last_used_ + lifetime_) { + SignalTimeout(this); + } else { + server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER); + } + + } else { + assert(false); + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h new file mode 100644 index 0000000..b99c632 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver.h @@ -0,0 +1,215 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __RELAYSERVER_H__ +#define __RELAYSERVER_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/socketaddresspair.h" +#include "talk/base/thread.h" +#include "talk/base/time.h" +#include "talk/p2p/base/stun.h" + +#include <string> +#include <vector> +#include <map> + +namespace cricket { + +class RelayServerBinding; +class RelayServerConnection; + +// Relays traffic between connections to the server that are "bound" together. +// All connections created with the same username/password are bound together. +class RelayServer : public sigslot::has_slots<> { +public: + // Creates a server, which will use this thread to post messages to itself. + RelayServer(talk_base::Thread* thread); + ~RelayServer(); + + talk_base::Thread* thread() { return thread_; } + + // Indicates whether we will print updates of the number of bindings. + bool log_bindings() const { return log_bindings_; } + void set_log_bindings(bool log_bindings) { log_bindings_ = log_bindings; } + + // Updates the set of sockets that the server uses to talk to "internal" + // clients. These are clients that do the "port allocations". + void AddInternalSocket(talk_base::AsyncPacketSocket* socket); + void RemoveInternalSocket(talk_base::AsyncPacketSocket* socket); + + // Updates the set of sockets that the server uses to talk to "external" + // clients. These are the clients that do not do allocations. They do not + // know that these addresses represent a relay server. + void AddExternalSocket(talk_base::AsyncPacketSocket* socket); + void RemoveExternalSocket(talk_base::AsyncPacketSocket* socket); + +private: + typedef std::vector<talk_base::AsyncPacketSocket*> SocketList; + typedef std::map<std::string,RelayServerBinding*> BindingMap; + typedef std::map<talk_base::SocketAddressPair,RelayServerConnection*> ConnectionMap; + + talk_base::Thread* thread_; + bool log_bindings_; + SocketList internal_sockets_; + SocketList external_sockets_; + BindingMap bindings_; + ConnectionMap connections_; + + // Called when a packet is received by the server on one of its sockets. + void OnInternalPacket( + const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + void OnExternalPacket( + const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + + // Processes the relevant STUN request types from the client. + bool HandleStun(const char* bytes, size_t size, + const talk_base::SocketAddress& remote_addr, talk_base::AsyncPacketSocket* socket, + std::string* username, StunMessage* msg); + void HandleStunAllocate(const char* bytes, size_t size, + const talk_base::SocketAddressPair& ap, + talk_base::AsyncPacketSocket* socket); + void HandleStun(RelayServerConnection* int_conn, const char* bytes, + size_t size); + void HandleStunAllocate(RelayServerConnection* int_conn, + const StunMessage& msg); + void HandleStunSend(RelayServerConnection* int_conn, const StunMessage& msg); + + // Adds/Removes the a connection or binding. + void AddConnection(RelayServerConnection* conn); + void RemoveConnection(RelayServerConnection* conn); + void RemoveBinding(RelayServerBinding* binding); + + // Called when the timer for checking lifetime times out. + void OnTimeout(RelayServerBinding* binding); + + friend class RelayServerConnection; + friend class RelayServerBinding; +}; + +// Maintains information about a connection to the server. Each connection is +// part of one and only one binding. +class RelayServerConnection { +public: + RelayServerConnection(RelayServerBinding* binding, + const talk_base::SocketAddressPair& addrs, + talk_base::AsyncPacketSocket* socket); + ~RelayServerConnection(); + + RelayServerBinding* binding() { return binding_; } + talk_base::AsyncPacketSocket* socket() { return socket_; } + + // Returns a pair where the source is the remote address and the destination + // is the local address. + const talk_base::SocketAddressPair& addr_pair() { return addr_pair_; } + + // Sends a packet to the connected client. If an address is provided, then + // we make sure the internal client receives it, wrapping if necessary. + void Send(const char* data, size_t size); + void Send(const char* data, size_t size, const talk_base::SocketAddress& ext_addr); + + // Sends a STUN message to the connected client with no wrapping. + void SendStun(const StunMessage& msg); + void SendStunError(const StunMessage& request, int code, const char* desc); + + // A locked connection is one for which we know the intended destination of + // any raw packet received. + bool locked() const { return locked_; } + void Lock(); + void Unlock(); + + // Records the address that raw packets should be forwarded to (for internal + // packets only; for external, we already know where they go). + const talk_base::SocketAddress& default_destination() const { return default_dest_; } + void set_default_destination(const talk_base::SocketAddress& addr) { + default_dest_ = addr; + } + +private: + RelayServerBinding* binding_; + talk_base::SocketAddressPair addr_pair_; + talk_base::AsyncPacketSocket* socket_; + bool locked_; + talk_base::SocketAddress default_dest_; +}; + +// Records a set of internal and external connections that we relay between, +// or in other words, that are "bound" together. +class RelayServerBinding : public talk_base::MessageHandler { +public: + RelayServerBinding( + RelayServer* server, const std::string& username, + const std::string& password, uint32 lifetime); + virtual ~RelayServerBinding(); + + RelayServer* server() { return server_; } + uint32 lifetime() { return lifetime_; } + const std::string& username() { return username_; } + const std::string& password() { return password_; } + const std::string& magic_cookie() { return magic_cookie_; } + + // Adds/Removes a connection into the binding. + void AddInternalConnection(RelayServerConnection* conn); + void AddExternalConnection(RelayServerConnection* conn); + + // We keep track of the use of each binding. If we detect that it was not + // used for longer than the lifetime, then we send a signal. + void NoteUsed(); + sigslot::signal1<RelayServerBinding*> SignalTimeout; + + // Determines whether the given packet has the magic cookie present (in the + // right place). + bool HasMagicCookie(const char* bytes, size_t size) const; + + // Determines the connection to use to send packets to or from the given + // external address. + RelayServerConnection* GetInternalConnection(const talk_base::SocketAddress& ext_addr); + RelayServerConnection* GetExternalConnection(const talk_base::SocketAddress& ext_addr); + + // MessageHandler: + void OnMessage(talk_base::Message *pmsg); + +private: + RelayServer* server_; + + std::string username_; + std::string password_; + std::string magic_cookie_; + + std::vector<RelayServerConnection*> internal_connections_; + std::vector<RelayServerConnection*> external_connections_; + + uint32 lifetime_; + uint32 last_used_; + // TODO: bandwidth +}; + +} // namespace cricket + +#endif // __RELAYSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc new file mode 100644 index 0000000..cdd5fff --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/relayserver_main.cc @@ -0,0 +1,75 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <cassert> +#include <iostream> +#include "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/relayserver.h" + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char **argv) { + if (argc != 1) { + std::cerr << "usage: relayserver" << std::endl; + return 1; + } + + assert(talk_base::LocalHost().networks().size() >= 2); + talk_base::SocketAddress int_addr(talk_base::LocalHost().networks()[1]->ip(), 5000); + talk_base::SocketAddress ext_addr(talk_base::LocalHost().networks()[1]->ip(), 5001); + + talk_base::Thread *pthMain = talk_base::Thread::Current(); + + talk_base::AsyncUDPSocket* int_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); + if (int_socket->Bind(int_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + talk_base::AsyncUDPSocket* ext_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); + if (ext_socket->Bind(ext_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + RelayServer server(pthMain); + server.AddInternalSocket(int_socket); + server.AddExternalSocket(ext_socket); + + std::cout << "Listening internally at " << int_addr.ToString() << std::endl; + std::cout << "Listening externally at " << ext_addr.ToString() << std::endl; + + pthMain->Run(); + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session.cc b/Plugins/jingle/libjingle/talk/p2p/base/session.cc new file mode 100644 index 0000000..1ea0dc3 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/session.cc @@ -0,0 +1,1029 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/session.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/helpers.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/jid.h" +#include "talk/p2p/base/sessionclient.h" +#include "talk/p2p/base/transport.h" +#include "talk/p2p/base/transportchannelproxy.h" +#include "talk/p2p/base/p2ptransport.h" +#include "talk/p2p/base/constants.h" + +namespace { + +const uint32 MSG_TIMEOUT = 1; +const uint32 MSG_ERROR = 2; +const uint32 MSG_STATE = 3; + +// This will be initialized at run time to hold the list of default transports. +std::string* gDefaultTransports = NULL; +size_t gNumDefaultTransports = 0; + +} // namespace + +namespace cricket { + +Session::Session(SessionManager *session_manager, const std::string& name, + const SessionID& id, const std::string& session_type, + SessionClient* client) { + ASSERT(session_manager->signaling_thread()->IsCurrent()); + ASSERT(client != NULL); + session_manager_ = session_manager; + name_ = name; + id_ = id; + session_type_ = session_type; + client_ = client; + error_ = ERROR_NONE; + state_ = STATE_INIT; + initiator_ = false; + description_ = NULL; + remote_description_ = NULL; + transport_ = NULL; + compatibility_mode_ = false; +} + +Session::~Session() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + ASSERT(state_ != STATE_DEINIT); + state_ = STATE_DEINIT; + SignalState(this, state_); + + delete description_; + delete remote_description_; + + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + iter->second->SignalDestroyed(iter->second); + delete iter->second; + } + + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + delete *iter; + } + + delete transport_; +} + +bool Session::Initiate(const std::string &to, + std::vector<buzz::XmlElement*>* extra_xml, + const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only from STATE_INIT + if (state_ != STATE_INIT) + return false; + + // Setup for signaling. + remote_name_ = to; + initiator_ = true; + description_ = description; + + // Make sure we have transports to negotiate. + CreateTransports(); + + // Send the initiate message, including the application and transport offers. + XmlElements elems; + elems.push_back(client_->TranslateSessionDescription(description)); + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + buzz::XmlElement* elem = (*iter)->CreateTransportOffer(); + elems.push_back(elem); + } + + if (extra_xml != NULL) { + std::vector<buzz::XmlElement*>::iterator iter = extra_xml->begin(); + for (std::vector<buzz::XmlElement*>::iterator iter = extra_xml->begin(); + iter != extra_xml->end(); + ++iter) { + elems.push_back(new buzz::XmlElement(**iter)); + } + } + + SendSessionMessage("initiate", elems); + + SetState(Session::STATE_SENTINITIATE); + + // We speculatively start attempting connection of the P2P transports. + ConnectDefaultTransportChannels(true); + return true; +} + +void Session::ConnectDefaultTransportChannels(bool create) { + Transport* transport = GetTransport(kNsP2pTransport); + if (transport) { + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + ASSERT(create != transport->HasChannel(iter->first)); + if (create) { + transport->CreateChannel(iter->first, session_type()); + } + } + transport->ConnectChannels(); + } +} + +void Session::CreateDefaultTransportChannel(const std::string& name) { + // This method is only relevant when we have created the default transport + // but not received a transport-accept. + ASSERT(transport_ == NULL); + ASSERT(state_ == STATE_SENTINITIATE); + + Transport* p2p_transport = GetTransport(kNsP2pTransport); + if (p2p_transport) { + ASSERT(!p2p_transport->HasChannel(name)); + p2p_transport->CreateChannel(name, session_type()); + } +} + +bool Session::Accept(const SessionDescription *description) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Only if just received initiate + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + // Setup for signaling. + initiator_ = false; + description_ = description; + + // If we haven't selected a transport, wait for ChooseTransport to complete + if (transport_ == NULL) + return true; + + // Send the accept message. + XmlElements elems; + elems.push_back(client_->TranslateSessionDescription(description_)); + SendSessionMessage("accept", elems); + SetState(Session::STATE_SENTACCEPT); + + return true; +} + +bool Session::Reject() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Reject is sent in response to an initiate or modify, to reject the + // request + if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY) + return false; + + // Setup for signaling. + initiator_ = false; + + // Send the reject message. + SendSessionMessage("reject", XmlElements()); + SetState(STATE_SENTREJECT); + + return true; +} + +bool Session::Redirect(const std::string & target) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Redirect is sent in response to an initiate or modify, to redirect the + // request + if (state_ != STATE_RECEIVEDINITIATE) + return false; + + // Setup for signaling. + initiator_ = false; + + // Send a redirect message to the given target. We include an element that + // names the redirector (us), which may be useful to the other side. + + buzz::XmlElement* target_elem = new buzz::XmlElement(QN_REDIRECT_TARGET); + target_elem->AddAttr(buzz::QN_NAME, target); + + buzz::XmlElement* cookie = new buzz::XmlElement(QN_REDIRECT_COOKIE); + buzz::XmlElement* regarding = new buzz::XmlElement(QN_REDIRECT_REGARDING); + regarding->AddAttr(buzz::QN_NAME, name_); + cookie->AddElement(regarding); + + XmlElements elems; + elems.push_back(target_elem); + elems.push_back(cookie); + SendSessionMessage("redirect", elems); + + // A redirect puts us in the same state as reject. It just sends a different + // kind of reject message, if you like. + SetState(STATE_SENTREDIRECT); + + return true; +} + +bool Session::Terminate() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Either side can terminate, at any time. + switch (state_) { + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + return false; + + case STATE_SENTREDIRECT: + // We must not send terminate if we redirect. + break; + + case STATE_SENTREJECT: + case STATE_RECEIVEDREJECT: + // We don't need to send terminate if we sent or received a reject... + // it's implicit. + break; + + default: + SendSessionMessage("terminate", XmlElements()); + break; + } + + SetState(STATE_SENTTERMINATE); + return true; +} + +void Session::SendInfoMessage(const XmlElements& elems) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SendSessionMessage("info", elems); +} + +void Session::SetPotentialTransports(const std::string names[], size_t length) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + for (size_t i = 0; i < length; ++i) { + Transport* transport = NULL; + if (names[i] == kNsP2pTransport) { + transport = new P2PTransport(session_manager_); + } else { + ASSERT(false); + } + + if (transport) { + ASSERT(transport->name() == names[i]); + potential_transports_.push_back(transport); + transport->SignalConnecting.connect( + this, &Session::OnTransportConnecting); + transport->SignalWritableState.connect( + this, &Session::OnTransportWritable); + transport->SignalRequestSignaling.connect( + this, &Session::OnTransportRequestSignaling); + transport->SignalTransportMessage.connect( + this, &Session::OnTransportSendMessage); + transport->SignalTransportError.connect( + this, &Session::OnTransportSendError); + transport->SignalChannelGone.connect( + this, &Session::OnTransportChannelGone); + } + } +} + +Transport* Session::GetTransport(const std::string& name) { + if (transport_ != NULL) { + if (name == transport_->name()) + return transport_; + } else { + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + if (name == (*iter)->name()) + return *iter; + } + } + return NULL; +} + +TransportChannel* Session::CreateChannel(const std::string& name) { + //ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(channels_.find(name) == channels_.end()); + TransportChannelProxy* channel = new TransportChannelProxy(name, session_type_); + channels_[name] = channel; + if (transport_) { + ASSERT(!transport_->HasChannel(name)); + channel->SetImplementation(transport_->CreateChannel(name, session_type_)); + } else if (state_ == STATE_SENTINITIATE) { + // In this case, we have already speculatively created the default + // transport. We should create this channel as well so that it may begin + // early connection. + CreateDefaultTransportChannel(name); + } + return channel; +} + +TransportChannel* Session::GetChannel(const std::string& name) { + ChannelMap::iterator iter = channels_.find(name); + return (iter != channels_.end()) ? iter->second : NULL; +} + +void Session::DestroyChannel(TransportChannel* channel) { + ChannelMap::iterator iter = channels_.find(channel->name()); + ASSERT(iter != channels_.end()); + ASSERT(channel == iter->second); + channels_.erase(iter); + channel->SignalDestroyed(channel); + delete channel; +} + +TransportChannelImpl* Session::GetImplementation(TransportChannel* channel) { + ChannelMap::iterator iter = channels_.find(channel->name()); + return (iter != channels_.end()) ? iter->second->impl() : NULL; +} + +void Session::CreateTransports() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT((state_ == STATE_INIT) + || (state_ == STATE_RECEIVEDINITIATE)); + if (potential_transports_.empty()) { + if (gDefaultTransports == NULL) { + gNumDefaultTransports = 1; + gDefaultTransports = new std::string[1]; + gDefaultTransports[0] = kNsP2pTransport; + } + SetPotentialTransports(gDefaultTransports, gNumDefaultTransports); + } +} + +bool Session::ChooseTransport(const buzz::XmlElement* stanza) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(state_ == STATE_RECEIVEDINITIATE); + ASSERT(transport_ == NULL); + + // Make sure we have decided on our own transports. + CreateTransports(); + + // Retrieve the session message. + const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION); + ASSERT(session != NULL); + + // Try the offered transports until we find one that we support. + bool found_offer = false; + const buzz::XmlElement* elem = session->FirstElement(); + while (elem) { + if (elem->Name().LocalPart() == "transport") { + found_offer = true; + Transport* transport = GetTransport(elem->Name().Namespace()); + if (transport && transport->OnTransportOffer(elem)) { + SetTransport(transport); + break; + } + } + elem = elem->NextElement(); + } + + // If the offer did not include any transports, then we are talking to an + // old client. In that case, we turn on compatibility mode, and we assume + // an offer containing just P2P, which is all that old clients support. + if (!found_offer) { + compatibility_mode_ = true; + + Transport* transport = GetTransport(kNsP2pTransport); + ASSERT(transport != NULL); + + scoped_ptr<buzz::XmlElement> transport_offer( + new buzz::XmlElement(kQnP2pTransport, true)); + bool valid = transport->OnTransportOffer(transport_offer.get()); + ASSERT(valid); + if (valid) + SetTransport(transport); + } + + if (!transport_) { + SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ACCEPTABLE, "modify", + "no supported transport in offer", NULL); + return false; + } + + // Get the description of the transport we picked. + buzz::XmlElement* answer = transport_->CreateTransportAnswer(); + ASSERT(answer->Name() == buzz::QName(transport_->name(), "transport")); + + // Send a transport-accept message telling the other side our decision, + // unless this is an old client that is not expecting one. + if (!compatibility_mode_) { + XmlElements elems; + elems.push_back(answer); + SendSessionMessage("transport-accept", elems); + } + + // If the user wants to accept, allow that now + if (description_) { + Accept(description_); + } + + return true; +} + +void Session::SetTransport(Transport* transport) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(transport_ == NULL); + transport_ = transport; + + // Drop the transports that were not selected. + bool found = false; + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + if (*iter == transport_) { + found = true; + } else { + delete *iter; + } + } + potential_transports_.clear(); + + // We require the selected transport to be one of the potential transports + ASSERT(found); + + // Create implementations for all of the channels if they don't exist. + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + TransportChannelProxy* channel = iter->second; + TransportChannelImpl* impl = transport_->GetChannel(channel->name()); + if (impl == NULL) + impl = transport_->CreateChannel(channel->name(), session_type()); + ASSERT(impl != NULL); + channel->SetImplementation(impl); + } + + // Have this transport start connecting if it is not already. + // (We speculatively connect the most common transport right away.) + transport_->ConnectChannels(); +} + +void Session::SetState(State state) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (state != state_) { + state_ = state; + SignalState(this, state_); + session_manager_->signaling_thread()->Post(this, MSG_STATE); + } +} + +void Session::SetError(Error error) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (error != error_) { + error_ = error; + SignalError(this, error); + if (error_ != ERROR_NONE) + session_manager_->signaling_thread()->Post(this, MSG_ERROR); + } +} + +void Session::OnSignalingReady() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + // Forward this to every transport. Those that did not request it should + // ignore this call. + if (transport_ != NULL) { + transport_->OnSignalingReady(); + } else { + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + (*iter)->OnSignalingReady(); + } + } +} + +void Session::OnTransportConnecting(Transport* transport) { + // This is an indication that we should begin watching the writability + // state of the transport. + OnTransportWritable(transport); +} + +void Session::OnTransportWritable(Transport* transport) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT((NULL == transport_) || (transport == transport_)); + + // If the transport is not writable, start a timer to make sure that it + // becomes writable within a reasonable amount of time. If it does not, we + // terminate since we can't actually send data. If the transport is writable, + // cancel the timer. Note that writability transitions may occur repeatedly + // during the lifetime of the session. + + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + if (transport->HasChannels() && !transport->writable()) { + session_manager_->signaling_thread()->PostDelayed( + session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + } +} + +void Session::OnTransportRequestSignaling(Transport* transport) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SignalRequestSignaling(this); +} + +void Session::OnTransportSendMessage(Transport* transport, + const XmlElements& elems) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + for (size_t i = 0; i < elems.size(); ++i) + ASSERT(elems[i]->Name() == buzz::QName(transport->name(), "transport")); + + if (compatibility_mode_) { + // In backward compatibility mode, we send a candidates message. + XmlElements candidates; + for (size_t i = 0; i < elems.size(); ++i) { + for (const buzz::XmlElement* elem = elems[i]->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + ASSERT(elem->Name() == kQnP2pCandidate); + + // Convert this candidate to an old style candidate (namespace change) + buzz::XmlElement* legacy_candidate = new buzz::XmlElement(*elem); + legacy_candidate->SetName(kQnLegacyCandidate); + candidates.push_back(legacy_candidate); + } + delete elems[i]; + } + + SendSessionMessage("candidates", candidates); + } else { + // If we haven't finished negotiation, then we may later discover that we + // need compatibility mode, in which case, we will need to re-send these. + if ((transport_ == NULL) && (transport->name() == kNsP2pTransport)) { + for (size_t i = 0; i < elems.size(); ++i) + candidates_.push_back(new buzz::XmlElement(*elems[i])); + } + + SendSessionMessage("transport-info", elems); + } +} + +void Session::OnTransportSendError(Transport* transport, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SignalErrorMessage(this, stanza, name, type, text, extra_info); +} + +void Session::OnTransportChannelGone(Transport* transport, + const std::string& name) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SignalChannelGone(this, name); +} + +void Session::SendSessionMessage( + const std::string& type, const std::vector<buzz::XmlElement*>& elems) { + scoped_ptr<buzz::XmlElement> iq(new buzz::XmlElement(buzz::QN_IQ)); + iq->SetAttr(buzz::QN_TO, remote_name_); + iq->SetAttr(buzz::QN_TYPE, buzz::STR_SET); + + buzz::XmlElement *session = new buzz::XmlElement(QN_SESSION, true); + session->AddAttr(buzz::QN_TYPE, type); + session->AddAttr(buzz::QN_ID, id_.id_str()); + session->AddAttr(QN_INITIATOR, id_.initiator()); + + for (size_t i = 0; i < elems.size(); ++i) + session->AddElement(elems[i]); + + iq->AddElement(session); + SignalOutgoingMessage(this, iq.get()); +} + +void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) { + scoped_ptr<buzz::XmlElement> ack(new buzz::XmlElement(buzz::QN_IQ)); + ack->SetAttr(buzz::QN_TO, remote_name_); + ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); + ack->SetAttr(buzz::QN_TYPE, "result"); + + SignalOutgoingMessage(this, ack.get()); +} + +void Session::OnIncomingMessage(const buzz::XmlElement* stanza) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(stanza->Name() == buzz::QN_IQ); + buzz::Jid remote(remote_name_); + buzz::Jid from(stanza->Attr(buzz::QN_FROM)); + ASSERT(state_ == STATE_INIT || from == remote); + + const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION); + ASSERT(session != NULL); + + if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET) { + ASSERT(false); + return; + } + + ASSERT(session->HasAttr(buzz::QN_TYPE)); + std::string type = session->Attr(buzz::QN_TYPE); + + bool valid = false; + + if (type == "initiate") { + valid = OnInitiateMessage(stanza, session); + } else if (type == "accept") { + valid = OnAcceptMessage(stanza, session); + } else if (type == "reject") { + valid = OnRejectMessage(stanza, session); + } else if (type == "redirect") { + valid = OnRedirectMessage(stanza, session); + } else if (type == "info") { + valid = OnInfoMessage(stanza, session); + } else if (type == "transport-accept") { + valid = OnTransportAcceptMessage(stanza, session); + } else if (type == "transport-info") { + valid = OnTransportInfoMessage(stanza, session); + } else if (type == "terminate") { + valid = OnTerminateMessage(stanza, session); + } else if (type == "candidates") { + // This is provided for backward compatibility. + // TODO: Remove this once old candidates are gone. + valid = OnCandidatesMessage(stanza, session); + } else { + SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session message type", NULL); + } + + // If the message was not valid, we should have sent back an error above. + // If it was valid, then we send an acknowledgement here. + if (valid) + SendAcknowledgementMessage(stanza); +} + +void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + + const buzz::XmlElement* orig_session = orig_stanza->FirstNamed(QN_SESSION); + ASSERT(orig_session != NULL); + + std::string error_type = "cancel"; + + const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); + ASSERT(error != NULL); + if (error) { + ASSERT(error->HasAttr(buzz::QN_TYPE)); + error_type = error->Attr(buzz::QN_TYPE); + + LOG(LERROR) << "Session error:\n" << error->Str() << "\n" + << "in response to:\n" << orig_session->Str(); + } + + bool fatal_error = false; + + ASSERT(orig_session->HasAttr(buzz::QN_TYPE)); + if ((orig_session->Attr(buzz::QN_TYPE) == "transport-info") + || (orig_session->Attr(buzz::QN_TYPE) == "candidates")) { + // Transport messages frequently generate errors because they are sent right + // when we detect a network failure. For that reason, we ignore such + // errors, because if we do not establish writability again, we will + // terminate anyway. The exceptions are transport-specific error tags, + // which we pass on to the respective transport. + for (const buzz::XmlElement* elem = error->FirstElement(); + NULL != elem; elem = elem->NextElement()) { + if (Transport* transport = GetTransport(elem->Name().Namespace())) { + if (!transport->OnTransportError(orig_session, elem)) { + fatal_error = true; + break; + } + } + } + } else if ((error_type != "continue") && (error_type != "wait")) { + // We do not set an error if the other side said it is okay to continue + // (possibly after waiting). These errors can be ignored. + fatal_error = true; + } + + if (fatal_error) { + SetError(ERROR_RESPONSE); + } +} + +bool Session::OnInitiateMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + if (!CheckState(stanza, STATE_INIT)) + return false; + if (!FindRemoteSessionDescription(stanza, session)) + return false; + + initiator_ = false; + remote_name_ = stanza->Attr(buzz::QN_FROM); + SetState(STATE_RECEIVEDINITIATE); + return true; +} + +bool Session::OnAcceptMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + if (!CheckState(stanza, STATE_SENTINITIATE)) + return false; + if (!FindRemoteSessionDescription(stanza, session)) + return false; + + SetState(STATE_RECEIVEDACCEPT); + return true; +} + +bool Session::OnRejectMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + if (!CheckState(stanza, STATE_SENTINITIATE)) + return false; + + SetState(STATE_RECEIVEDREJECT); + return true; +} + +bool Session::OnRedirectMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + if (!CheckState(stanza, STATE_SENTINITIATE)) + return false; + + const buzz::XmlElement *redirect_target; + if (!FindRequiredElement(stanza, session, QN_REDIRECT_TARGET, + &redirect_target)) + return false; + + if (!FindRequiredAttribute(stanza, redirect_target, buzz::QN_NAME, + &remote_name_)) + return false; + + const buzz::XmlElement* redirect_cookie = + session->FirstNamed(QN_REDIRECT_COOKIE); + + XmlElements elems; + elems.push_back(client_->TranslateSessionDescription(description_)); + if (redirect_cookie) + elems.push_back(new buzz::XmlElement(*redirect_cookie)); + SendSessionMessage("initiate", elems); + + // Clear the connection timeout (if any). We will start the connection + // timer from scratch when SignalConnecting fires. + session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT); + + // Reset all of the sockets back into the initial state. + for (TransportList::iterator iter = potential_transports_.begin(); + iter != potential_transports_.end(); + ++iter) { + (*iter)->ResetChannels(); + } + + ConnectDefaultTransportChannels(false); + return true; +} + +bool Session::OnInfoMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + XmlElements elems; + for (const buzz::XmlElement* elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + elems.push_back(new buzz::XmlElement(*elem)); + } + + SignalInfoMessage(this, elems); + return true; +} + +bool Session::OnTransportAcceptMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + if (!CheckState(stanza, STATE_SENTINITIATE)) + return false; + + Transport* transport = NULL; + const buzz::XmlElement* transport_elem = NULL; + + for(const buzz::XmlElement* elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name().LocalPart() == "transport") { + Transport* transport = GetTransport(elem->Name().Namespace()); + if (transport) { + if (transport_elem) { // trying to accept two transport? + SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, + "modify", "transport-accept has two answers", + NULL); + return false; + } + + transport_elem = elem; + if (!transport->OnTransportAnswer(transport_elem)) { + SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, + "modify", "transport-accept is not acceptable", + NULL); + return false; + } + SetTransport(transport); + } + } + } + + if (!transport_elem) { + SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ALLOWED, "modify", + "no supported transport in answer", NULL); + return false; + } + + // If we discovered that we need compatibility mode and we have sent some + // candidates already (using transport-info), then we need to re-send them + // using the candidates message. + if (compatibility_mode_ && (candidates_.size() > 0)) { + ASSERT(transport_ != NULL); + ASSERT(transport_->name() == kNsP2pTransport); + OnTransportSendMessage(transport_, candidates_); + } else { + for (size_t i = 0; i < candidates_.size(); ++i) + delete candidates_[i]; + } + candidates_.clear(); + + return true; +} + +bool Session::OnTransportInfoMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + for(const buzz::XmlElement* elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name().LocalPart() == "transport") { + Transport* transport = GetTransport(elem->Name().Namespace()); + if (transport) { + if (!transport->OnTransportMessage(elem, stanza)) + return false; + } + } + } + return true; +} + +bool Session::OnTerminateMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + for (const buzz::XmlElement *elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + // elem->Name().LocalPart() is the reason for termination + SignalReceivedTerminateReason(this, elem->Name().LocalPart()); + // elem->FirstElement() might contain a debug string for termination + const buzz::XmlElement *debugElement = elem->FirstElement(); + if (debugElement != NULL) { + LOG(LS_VERBOSE) << "Received error on call: " + << debugElement->Name().LocalPart(); + } + } + SetState(STATE_RECEIVEDTERMINATE); + return true; +} + +bool Session::OnCandidatesMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + // If we don't have a transport, then this is the first candidates message. + // We first create a fake transport-accept message in order to finish the + // negotiation and create a transport. + if (!transport_) { + compatibility_mode_ = true; + + scoped_ptr<buzz::XmlElement> transport_accept( + new buzz::XmlElement(QN_SESSION)); + transport_accept->SetAttr(buzz::QN_TYPE, "transport-accept"); + + buzz::XmlElement* transport_offer = + new buzz::XmlElement(kQnP2pTransport, true); + transport_accept->AddElement(transport_offer); + + // It is okay to pass the original stanza here. That is only used if we + // send an error message. Normal processing looks only at transport_accept. + bool valid = OnTransportAcceptMessage(stanza, transport_accept.get()); + ASSERT(valid); + } + + ASSERT(transport_ != NULL); + ASSERT(transport_->name() == kNsP2pTransport); + + // Wrap the candidates in a transport element as they would appear in a + // transport-info message and send this to the transport. + scoped_ptr<buzz::XmlElement> transport_info( + new buzz::XmlElement(kQnP2pTransport, true)); + for (const buzz::XmlElement* elem = session->FirstNamed(kQnLegacyCandidate); + elem != NULL; + elem = elem->NextNamed(kQnLegacyCandidate)) { + buzz::XmlElement* new_candidate = new buzz::XmlElement(*elem); + new_candidate->SetName(kQnP2pCandidate); + transport_info->AddElement(new_candidate); + } + return transport_->OnTransportMessage(transport_info.get(), stanza); +} + +bool Session::CheckState(const buzz::XmlElement* stanza, State state) { + ASSERT(state_ == state); + if (state_ != state) { + SignalErrorMessage(this, stanza, buzz::QN_STANZA_NOT_ALLOWED, "modify", + "message not allowed in current state", NULL); + return false; + } + return true; +} + +bool Session::FindRequiredElement(const buzz::XmlElement* stanza, + const buzz::XmlElement* parent, + const buzz::QName& name, + const buzz::XmlElement** elem) { + *elem = parent->FirstNamed(name); + if (*elem == NULL) { + std::string text; + text += "element '" + parent->Name().Merged() + + "' missing required child '" + name.Merged() + "'"; + SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + text, NULL); + return false; + } + return true; +} + +bool Session::FindRemoteSessionDescription(const buzz::XmlElement* stanza, + const buzz::XmlElement* session) { + buzz::QName qn_session(session_type_, "description"); + const buzz::XmlElement* desc; + if (!FindRequiredElement(stanza, session, qn_session, &desc)) + return false; + remote_description_ = client_->CreateSessionDescription(desc); + return true; +} + +bool Session::FindRequiredAttribute(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + const buzz::QName& name, + std::string* value) { + if (!elem->HasAttr(name)) { + std::string text; + text += "element '" + elem->Name().Merged() + + "' missing required attribute '" + name.Merged() + "'"; + SignalErrorMessage(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + text, NULL); + return false; + } else { + *value = elem->Attr(name); + return true; + } +} + +void Session::OnMessage(talk_base::Message *pmsg) { + switch(pmsg->message_id) { + case MSG_TIMEOUT: + // Session timeout has occured. + SetError(ERROR_TIME); + break; + + case MSG_ERROR: + // Any of the defined errors is most likely fatal. + Terminate(); + break; + + case MSG_STATE: + switch (state_) { + case STATE_SENTACCEPT: + case STATE_RECEIVEDACCEPT: + SetState(STATE_INPROGRESS); + ASSERT(transport_ != NULL); + break; + + case STATE_SENTREJECT: + case STATE_SENTREDIRECT: + case STATE_RECEIVEDREJECT: + Terminate(); + break; + + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + session_manager_->DestroySession(this); + break; + + default: + // Explicitly ignoring some states here. + break; + } + break; + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session.h b/Plugins/jingle/libjingle/talk/p2p/base/session.h new file mode 100644 index 0000000..5146ea0 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/session.h @@ -0,0 +1,357 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SESSION_H_ +#define _SESSION_H_ + +#include "talk/base/socketaddress.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/sessionclient.h" +#include "talk/p2p/base/sessionid.h" +#include "talk/p2p/base/port.h" +#include "talk/xmllite/xmlelement.h" +#include <string> + +namespace cricket { + +class SessionManager; +class Transport; +class TransportChannel; +class TransportChannelProxy; +class TransportChannelImpl; + +// A specific Session created by the SessionManager. A Session manages +// signaling for session setup and tear down. This setup includes negotiation +// of both the application-level and network-level protocols: the former +// defines what will be sent and the latter defines how it will be sent. Each +// network-level protocol is represented by a Transport object. Each Transport +// participates in the network-level negotiation. The individual streams of +// packets are represented by TransportChannels. +class Session : public talk_base::MessageHandler, public sigslot::has_slots<> { + public: + enum State { + STATE_INIT = 0, + STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject + STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject + STATE_SENTACCEPT, // sent accept. begin connecting transport + STATE_RECEIVEDACCEPT, // received accept. begin connecting transport + STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject + STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject + STATE_SENTREJECT, // sent reject after receiving initiate + STATE_RECEIVEDREJECT, // received reject after sending initiate + STATE_SENTREDIRECT, // sent direct after receiving initiate + STATE_SENTTERMINATE, // sent terminate (any time / either side) + STATE_RECEIVEDTERMINATE, // received terminate (any time / either side) + STATE_INPROGRESS, // session accepted and in progress + STATE_DEINIT, // session is being destroyed + }; + + enum Error { + ERROR_NONE = 0, // no error + ERROR_TIME, // no response to signaling + ERROR_RESPONSE, // error during signaling + ERROR_NETWORK, // network error, could not allocate network resources + }; + + // Returns the manager that created and owns this session. + SessionManager* session_manager() const { return session_manager_; } + + // Returns the XML namespace identifying the type of this session. + const std::string& session_type() const { return session_type_; } + + // Returns the client that is handling the application data of this session. + SessionClient* client() const { return client_; } + + // Returns the JID this client. + const std::string &name() const { return name_; } + + // Returns the JID of the other peer in this session. + const std::string &remote_name() const { return remote_name_; } + + // Indicates whether we initiated this session. + bool initiator() const { return initiator_; } + + // Holds the ID of this session, which should be unique across the world. + const SessionID& id() const { return id_; } + + // Returns the applicication-level description given by our client. This + // will be null until Initiate or Accept. + const SessionDescription *description() const { return description_; } + + // Returns the applicication-level description given by the other client. + // If we are the initiator, this will be null until we receive an accept. + const SessionDescription *remote_description() const { + return remote_description_; + } + + // Returns the current state of the session. See the enum above for details. + // Each time the state changes, we will fire this signal. + State state() const { return state_; } + sigslot::signal2<Session *, State> SignalState; + + // Fired whenever we receive a terminate message along with a reason + sigslot::signal2<Session *, const std::string &> SignalReceivedTerminateReason; + + // Returns the last error in the session. See the enum above for details. + // Each time the an error occurs, we will fire this signal. + Error error() const { return error_; } + sigslot::signal2<Session *, Error> SignalError; + + // Returns the transport that has been negotiated or NULL if negotiation is + // still in progress. + Transport* transport() const { return transport_; } + + // When a session was created by us, we are the initiator, and we send the + // initiate message when this method is invoked. The extra_xml parameter is + // a list of elements that will get inserted inside <Session> ... </Session> + bool Initiate(const std::string &to, std::vector<buzz::XmlElement*>* extra_xml, + const SessionDescription *description); + + // When we receive a session initiation from another client, we create a + // session in the RECEIVEDINITIATE state. We respond by accepting, + // rejecting, or redirecting the session somewhere else. + bool Accept(const SessionDescription *description); + bool Reject(); + bool Redirect(const std::string& target); + + // At any time, we may terminate an outstanding session. + bool Terminate(); + + // The two clients in the session may also send one another arbitrary XML + // messages, which are called "info" messages. Both of these functions take + // ownership of the XmlElements and delete them when done. + typedef std::vector<buzz::XmlElement*> XmlElements; + void SendInfoMessage(const XmlElements& elems); + sigslot::signal2<Session*, const XmlElements&> SignalInfoMessage; + + // Controls the set of transports that will be allowed for this session. If + // we are initiating, then this list will be used to construct the transports + // that we will offer to the other side. In that case, the order of the + // transport names indicates our preference (first has highest preference). + // If we are receiving, then this list indicates the set of transports that + // we will allow. We will choose the first transport in the offered list + // (1) whose name appears in the given list and (2) that can accept the offer + // provided (which may include parameters particular to the transport). + // + // If this function is not called (or if it is called with a NULL array), + // then we will use a default set of transports. + void SetPotentialTransports(const std::string names[], size_t length); + + // Once transports have been created (by SetTransports), this function will + // return the transport with the given name or NULL if none was created. + // Once a particular transport has been chosen, only that transport will be + // returned. + Transport* GetTransport(const std::string& name); + + // Creates a new channel with the given name. This method may be called + // immediately after creating the session. However, the actual + // implementation may not be fixed until transport negotiation completes. + TransportChannel* CreateChannel(const std::string& name); + + // Returns the channel with the given name. + TransportChannel* GetChannel(const std::string& name); + + // Destroys the given channel. + void DestroyChannel(TransportChannel* channel); + + // Note: This function is a hack and should not be used. + TransportChannelImpl* GetImplementation(TransportChannel* channel); + + // Invoked when we notice that there is no matching channel on our peer. + sigslot::signal2<Session*, const std::string&> SignalChannelGone; + + private: + typedef std::list<Transport*> TransportList; + typedef std::map<std::string, TransportChannelProxy*> ChannelMap; + + SessionManager *session_manager_; + std::string name_; + std::string remote_name_; + bool initiator_; + SessionID id_; + std::string session_type_; + SessionClient* client_; + const SessionDescription *description_; + const SessionDescription *remote_description_; + State state_; + Error error_; + std::string redirect_target_; + // Note that the following two members are mutually exclusive + TransportList potential_transports_; // order implies preference + Transport* transport_; // negotiated transport + ChannelMap channels_; + bool compatibility_mode_; // indicates talking to an old client + XmlElements candidates_; // holds candidates sent in case of compat-mode + + // Creates or destroys a session. (These are called only SessionManager.) + Session(SessionManager *session_manager, + const std::string& name, + const SessionID& id, + const std::string& session_type, + SessionClient* client); + ~Session(); + + // Updates the state, signaling if necessary. + void SetState(State state); + + // Updates the error state, signaling if necessary. + void SetError(Error error); + + // To improve connection time, this creates the channels on the most common + // transport type and initiates connection. + void ConnectDefaultTransportChannels(bool create); + + // If a new channel is created after we have created the default transport, + // then we should create this channel as well and let it connect. + void CreateDefaultTransportChannel(const std::string& name); + + // Creates a default set of transports if the client did not specify some. + void CreateTransports(); + + // Attempts to choose a transport that is in both our list and the other + // clients. This will examine the children of the given XML element to find + // the descriptions of the other client's transports. We will pick the first + // transport in the other client's list that we also support. + // (This is called only by SessionManager.) + bool ChooseTransport(const buzz::XmlElement* msg); + + // Called when a single transport has been negotiated. + void SetTransport(Transport* transport); + + // Called when the first channel of a transport begins connecting. We use + // this to start a timer, to make sure that the connection completes in a + // reasonable amount of time. + void OnTransportConnecting(Transport* transport); + + // Called when a transport changes its writable state. We track this to make + // sure that the transport becomes writable within a reasonable amount of + // time. If this does not occur, we signal an error. + void OnTransportWritable(Transport* transport); + + // Called when a transport requests signaling. + void OnTransportRequestSignaling(Transport* transport); + + // Called when a transport signals that it has a message to send. Note that + // these messages are just the transport part of the stanza; they need to be + // wrapped in the appropriate session tags. + void OnTransportSendMessage(Transport* transport, const XmlElements& elems); + + // Called when a transport signals that it found an error in an incoming + // message. + void OnTransportSendError(Transport* transport, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + // Called when we notice that one of our local channels has no peer, so it + // should be destroyed. + void OnTransportChannelGone(Transport* transport, const std::string& name); + + // When the session needs to send signaling messages, it beings by requesting + // signaling. The client should handle this by calling OnSignalingReady once + // it is ready to send the messages. + // (These are called only by SessionManager.) + sigslot::signal1<Session*> SignalRequestSignaling; + void OnSignalingReady(); + + // Sends a message of the given type to the other client. The body will + // contain the given list of elements (which are consumed by the function). + void SendSessionMessage(const std::string& type, + const XmlElements& elems); + + // Sends a message back to the other client indicating that we have received + // and accepted their message. + void SendAcknowledgementMessage(const buzz::XmlElement* stanza); + + // Once signaling is ready, the session will use this signal to request the + // sending of each message. When messages are received by the other client, + // they should be handed to OnIncomingMessage. + // (These are called only by SessionManager.) + sigslot::signal2<Session *, const buzz::XmlElement*> SignalOutgoingMessage; + void OnIncomingMessage(const buzz::XmlElement* stanza); + + void OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza); + + // Invoked when an error is found in an incoming message. This is translated + // into the appropriate XMPP response by SessionManager. + sigslot::signal6<Session*, + const buzz::XmlElement*, + const buzz::QName&, + const std::string&, + const std::string&, + const buzz::XmlElement*> SignalErrorMessage; + + // Handlers for the various types of messages. These functions may take + // pointers to the whole stanza or to just the session element. + bool OnInitiateMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnAcceptMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnRejectMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnRedirectMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnInfoMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnTransportAcceptMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnTransportInfoMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnTerminateMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool OnCandidatesMessage(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + + // Helper functions for parsing various message types. CheckState verifies + // that we are in the appropriate state to receive this message. The latter + // three verify that an element has the required child or attribute. + bool CheckState(const buzz::XmlElement* stanza, State state); + bool FindRequiredElement(const buzz::XmlElement* stanza, + const buzz::XmlElement* parent, + const buzz::QName& name, + const buzz::XmlElement** elem); + bool FindRemoteSessionDescription(const buzz::XmlElement* stanza, + const buzz::XmlElement* session); + bool FindRequiredAttribute(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + const buzz::QName& name, + std::string* value); + + // Handles messages posted to us. + void OnMessage(talk_base::Message *pmsg); + + friend class SessionManager; // For access to constructor, destructor, + // and signaling related methods. +}; + +} // namespace cricket + +#endif // _SESSION_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc new file mode 100644 index 0000000..972ed9d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/session_unittest.cc @@ -0,0 +1,1039 @@ +#include <iostream> +#include <sstream> +#include <deque> +#include <map> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/host.h" +#include "talk/base/natserver.h" +#include "talk/base/natsocketfactory.h" +#include "talk/base/helpers.h" +#include "talk/xmpp/constants.h" +#include "talk/p2p/base/constants.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/sessionclient.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/transportchannel.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/p2ptransport.h" +#include "talk/p2p/base/rawtransport.h" +#include "talk/p2p/base/stunserver.h" +#include "talk/p2p/base/relayserver.h" + +using namespace cricket; +using namespace buzz; + +const std::string kSessionType = "http://oink.splat/session"; + +const talk_base::SocketAddress kStunServerAddress("127.0.0.1", 7000); +const talk_base::SocketAddress kStunServerAddress2("127.0.0.1", 7001); + +const talk_base::SocketAddress kRelayServerIntAddress("127.0.0.1", 7002); +const talk_base::SocketAddress kRelayServerExtAddress("127.0.0.1", 7003); + +const int kNumPorts = 2; + +int gPort = 28653; +int GetNextPort() { + int p = gPort; + gPort += 5; + return p; +} + +int gID = 0; +std::string GetNextID() { + std::ostringstream ost; + ost << gID++; + return ost.str(); +} + +class TestPortAllocatorSession : public PortAllocatorSession { +public: + TestPortAllocatorSession(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory, + const std::string& name, const std::string& session_type) + : PortAllocatorSession(0), worker_thread_(worker_thread), + factory_(factory), name_(name), ports_(kNumPorts), + address_("127.0.0.1", 0), network_("network", address_.ip()), + running_(false) { + } + + ~TestPortAllocatorSession() { + for (int i = 0; i < ports_.size(); i++) + delete ports_[i]; + } + + virtual void GetInitialPorts() { + // These are the flags set by the raw transport. + uint32 raw_flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP; + + // If the client doesn't care, just give them two UDP ports. + if (flags() == 0) { + for (int i = 0; i < kNumPorts; i++) { + ports_[i] = new UDPPort(worker_thread_, factory_, &network_, + GetAddress()); + AddPort(ports_[i]); + } + + // If the client requested just stun and relay, we have to oblidge. + } else if (flags() == raw_flags) { + StunPort* sport = new StunPort(worker_thread_, factory_, &network_, + GetAddress(), kStunServerAddress); + sport->set_server_addr2(kStunServerAddress2); + ports_[0] = sport; + AddPort(sport); + + std::string username = CreateRandomString(16); + std::string password = CreateRandomString(16); + RelayPort* rport = new RelayPort(worker_thread_, factory_, &network_, + GetAddress(), username, password, ""); + rport->AddServerAddress( + ProtocolAddress(kRelayServerIntAddress, PROTO_UDP)); + ports_[1] = rport; + AddPort(rport); + } else { + ASSERT(false); + } + } + + virtual void StartGetAllPorts() { running_ = true; } + virtual void StopGetAllPorts() { running_ = false; } + virtual bool IsGettingAllPorts() { return running_; } + + talk_base::SocketAddress GetAddress() const { + talk_base::SocketAddress addr(address_); + addr.SetPort(GetNextPort()); + return addr; + } + + void AddPort(Port* port) { + port->set_name(name_); + port->set_preference(1.0); + port->set_generation(0); + port->SignalDestroyed.connect( + this, &TestPortAllocatorSession::OnPortDestroyed); + port->SignalAddressReady.connect( + this, &TestPortAllocatorSession::OnAddressReady); + port->PrepareAddress(); + SignalPortReady(this, port); + } + + void OnPortDestroyed(Port* port) { + for (int i = 0; i < ports_.size(); i++) { + if (ports_[i] == port) + ports_[i] = NULL; + } + } + + void OnAddressReady(Port* port) { + SignalCandidatesReady(this, port->candidates()); + } + +private: + talk_base::Thread* worker_thread_; + talk_base::SocketFactory* factory_; + std::string name_; + std::vector<Port*> ports_; + talk_base::SocketAddress address_; + talk_base::Network network_; + bool running_; +}; + +class TestPortAllocator : public PortAllocator { +public: + TestPortAllocator(talk_base::Thread* worker_thread, talk_base::SocketFactory* factory) + : worker_thread_(worker_thread), factory_(factory) { + if (factory_ == NULL) + factory_ = worker_thread_->socketserver(); + } + + virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) { + return new TestPortAllocatorSession(worker_thread_, factory_, name, session_type); + } + +private: + talk_base::Thread* worker_thread_; + talk_base::SocketFactory* factory_; +}; + +struct SessionManagerHandler : sigslot::has_slots<> { + SessionManagerHandler(SessionManager* m, const std::string& u) + : manager(m), username(u), create_count(0), destroy_count(0) { + manager->SignalSessionCreate.connect( + this, &SessionManagerHandler::OnSessionCreate); + manager->SignalSessionDestroy.connect( + this, &SessionManagerHandler::OnSessionDestroy); + manager->SignalOutgoingMessage.connect( + this, &SessionManagerHandler::OnOutgoingMessage); + manager->SignalRequestSignaling.connect( + this, &SessionManagerHandler::OnRequestSignaling); + } + + void OnSessionCreate(Session *session, bool initiate) { + create_count += 1; + last_id = session->id(); + } + + void OnSessionDestroy(Session *session) { + destroy_count += 1; + last_id = session->id(); + } + + void OnOutgoingMessage(const XmlElement* stanza) { + XmlElement* elem = new XmlElement(*stanza); + ASSERT(elem->Name() == QN_IQ); + ASSERT(elem->HasAttr(QN_TO)); + ASSERT(!elem->HasAttr(QN_FROM)); + ASSERT(elem->HasAttr(QN_TYPE)); + ASSERT((elem->Attr(QN_TYPE) == "set") || + (elem->Attr(QN_TYPE) == "result") || + (elem->Attr(QN_TYPE) == "error")); + + // Add in the appropriate "from". + elem->SetAttr(QN_FROM, username); + + // Add in the appropriate IQ ID. + if (elem->Attr(QN_TYPE) == "set") { + ASSERT(!elem->HasAttr(QN_ID)); + elem->SetAttr(QN_ID, GetNextID()); + } + + stanzas_.push_back(elem); + } + + void OnRequestSignaling() { + manager->OnSignalingReady(); + } + + + XmlElement* CheckNextStanza(const std::string& expected) { + // Get the next stanza, which should exist. + ASSERT(stanzas_.size() > 0); + XmlElement* stanza = stanzas_.front(); + stanzas_.pop_front(); + + // Make sure the stanza is correct. + std::string actual = stanza->Str(); + if (actual != expected) { + LOG(LERROR) << "Incorrect stanza: expected=\"" << expected + << "\" actual=\"" << actual << "\""; + ASSERT(actual == expected); + } + + return stanza; + } + + void CheckNoStanza() { + ASSERT(stanzas_.size() == 0); + } + + void PrintNextStanza() { + ASSERT(stanzas_.size() > 0); + printf("Stanza: %s\n", stanzas_.front()->Str().c_str()); + } + + SessionManager* manager; + std::string username; + SessionID last_id; + uint32 create_count; + uint32 destroy_count; + std::deque<XmlElement*> stanzas_; +}; + +struct SessionHandler : sigslot::has_slots<> { + SessionHandler(Session* s) : session(s) { + session->SignalState.connect(this, &SessionHandler::OnState); + session->SignalError.connect(this, &SessionHandler::OnError); + } + + void PrepareTransport() { + Transport* transport = session->GetTransport(kNsP2pTransport); + if (transport != NULL) + transport->set_allow_local_ips(true); + } + + void OnState(Session* session, Session::State state) { + ASSERT(session == this->session); + last_state = state; + } + + void OnError(Session* session, Session::Error error) { + ASSERT(session == this->session); + ASSERT(false); // errors are bad! + } + + Session* session; + Session::State last_state; +}; + +struct MySessionClient: public SessionClient, public sigslot::has_slots<> { + MySessionClient() : create_count(0), a(NULL), b(NULL) { } + + void AddManager(SessionManager* manager) { + manager->AddClient(kSessionType, this); + ASSERT(manager->GetClient(kSessionType) == this); + manager->SignalSessionCreate.connect( + this, &MySessionClient::OnSessionCreate); + } + + const SessionDescription* CreateSessionDescription( + const XmlElement* element) { + return new SessionDescription(); + } + + XmlElement* TranslateSessionDescription( + const SessionDescription* description) { + return new XmlElement(QName(kSessionType, "description")); + } + + void OnSessionCreate(Session *session, bool initiate) { + create_count += 1; + a = session->CreateChannel("a"); + b = session->CreateChannel("b"); + + if (transport_name.size() > 0) + session->SetPotentialTransports(&transport_name, 1); + } + + void OnSessionDestroy(Session *session) + { + } + + void SetTransports(bool p2p, bool raw) { + if (p2p && raw) + return; // this is the default + + if (p2p) { + transport_name = kNsP2pTransport; + } + } + + int create_count; + TransportChannel* a; + TransportChannel* b; + std::string transport_name; +}; + +struct ChannelHandler : sigslot::has_slots<> { + ChannelHandler(TransportChannel* p) + : channel(p), last_readable(false), last_writable(false), data_count(0), + last_size(0) { + p->SignalReadableState.connect(this, &ChannelHandler::OnReadableState); + p->SignalWritableState.connect(this, &ChannelHandler::OnWritableState); + p->SignalReadPacket.connect(this, &ChannelHandler::OnReadPacket); + } + + void OnReadableState(TransportChannel* p) { + ASSERT(p == channel); + last_readable = channel->readable(); + } + + void OnWritableState(TransportChannel* p) { + ASSERT(p == channel); + last_writable = channel->writable(); + } + + void OnReadPacket(TransportChannel* p, const char* buf, size_t size) { + ASSERT(p == channel); + ASSERT(size <= sizeof(last_data)); + data_count += 1; + last_size = size; + std::memcpy(last_data, buf, size); + } + + void Send(const char* data, size_t size) { + int result = channel->SendPacket(data, size); + ASSERT(result == static_cast<int>(size)); + } + + TransportChannel* channel; + bool last_readable, last_writable; + int data_count; + char last_data[4096]; + size_t last_size; +}; + +char* Reverse(const char* str) { + int len = strlen(str); + char* rev = new char[len+1]; + for (int i = 0; i < len; i++) + rev[i] = str[len-i-1]; + rev[len] = '\0'; + return rev; +} + +// Sets up values that should be the same for every test. +void InitTest() { + SetRandomSeed(7); + gPort = 28653; + gID = 0; +} + +// Tests having client2 accept the session. +void TestAccept(talk_base::Thread* signaling_thread, + Session* session1, Session* session2, + SessionHandler* handler1, SessionHandler* handler2, + SessionManager* manager1, SessionManager* manager2, + SessionManagerHandler* manhandler1, + SessionManagerHandler* manhandler2) { + // Make sure the IQ ID is 5. + ASSERT(gID <= 5); + while (gID < 5) GetNextID(); + + // Accept the session. + SessionDescription* desc2 = new SessionDescription(); + bool valid = session2->Accept(desc2); + ASSERT(valid); + + scoped_ptr<buzz::XmlElement> stanza; + stanza.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"5\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"accept\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<ses:description xmlns:ses=\"http://oink.splat/session\"/>" + "</session>" + "</cli:iq>")); + manhandler2->CheckNoStanza(); + + // Simulate a tiny delay in sending. + signaling_thread->ProcessMessages(10); + + // Delivery the accept. + manager1->OnIncomingMessage(stanza.get()); + stanza.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" id=\"5\" type=\"result\" from=\"foo@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler1->CheckNoStanza(); + + // Both sessions should be in progress after a short wait. + signaling_thread->ProcessMessages(10); + ASSERT(handler1->last_state == Session::STATE_INPROGRESS); + ASSERT(handler2->last_state == Session::STATE_INPROGRESS); +} + +// Tests sending data between two clients, over two channels. +void TestSendRecv(ChannelHandler* chanhandler1a, ChannelHandler* chanhandler1b, + ChannelHandler* chanhandler2a, ChannelHandler* chanhandler2b, + talk_base::Thread* signaling_thread, bool first_dropped) { + const char* dat1a = "spamspamspamspamspamspamspambakedbeansspam"; + const char* dat1b = "Lobster Thermidor a Crevette with a mornay sauce..."; + const char* dat2a = Reverse(dat1a); + const char* dat2b = Reverse(dat1b); + + // Sending from 2 -> 1 will enable 1 to send to 2 below. That will then + // enable 2 to send back to 1. So the code below will just work. + if (first_dropped) { + chanhandler2a->Send(dat2a, strlen(dat2a)); + chanhandler2b->Send(dat2b, strlen(dat2b)); + } + + for (int i = 0; i < 20; i++) { + chanhandler1a->Send(dat1a, strlen(dat1a)); + chanhandler1b->Send(dat1b, strlen(dat1b)); + chanhandler2a->Send(dat2a, strlen(dat2a)); + chanhandler2b->Send(dat2b, strlen(dat2b)); + + signaling_thread->ProcessMessages(10); + + ASSERT(chanhandler1a->data_count == i + 1); + ASSERT(chanhandler1b->data_count == i + 1); + ASSERT(chanhandler2a->data_count == i + 1); + ASSERT(chanhandler2b->data_count == i + 1); + + ASSERT(chanhandler1a->last_size == strlen(dat2a)); + ASSERT(chanhandler1b->last_size == strlen(dat2b)); + ASSERT(chanhandler2a->last_size == strlen(dat1a)); + ASSERT(chanhandler2b->last_size == strlen(dat1b)); + + ASSERT(std::memcmp(chanhandler1a->last_data, dat2a, strlen(dat2a)) == 0); + ASSERT(std::memcmp(chanhandler1b->last_data, dat2b, strlen(dat2b)) == 0); + ASSERT(std::memcmp(chanhandler2a->last_data, dat1a, strlen(dat1a)) == 0); + ASSERT(std::memcmp(chanhandler2b->last_data, dat1b, strlen(dat1b)) == 0); + } +} + +// Tests a session between two clients. The inputs indicate whether we should +// replace each client's output with what we would see from an old client. +void TestP2PCompatibility(const std::string& test_name, bool old1, bool old2) { + InitTest(); + + talk_base::Thread* signaling_thread = talk_base::Thread::Current(); + scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread()); + worker_thread->Start(); + + scoped_ptr<PortAllocator> allocator( + new TestPortAllocator(worker_thread.get(), NULL)); + scoped_ptr<MySessionClient> client(new MySessionClient()); + client->SetTransports(true, false); + + scoped_ptr<SessionManager> manager1( + new SessionManager(allocator.get(), worker_thread.get())); + scoped_ptr<SessionManagerHandler> manhandler1( + new SessionManagerHandler(manager1.get(), "foo@baz.com")); + client->AddManager(manager1.get()); + + Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType); + ASSERT(manhandler1->create_count == 1); + ASSERT(manhandler1->last_id == session1->id()); + scoped_ptr<SessionHandler> handler1(new SessionHandler(session1)); + + ASSERT(client->create_count == 1); + TransportChannel* chan1a = client->a; + ASSERT(chan1a->name() == "a"); + ASSERT(session1->GetChannel("a") == chan1a); + scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a)); + TransportChannel* chan1b = client->b; + ASSERT(chan1b->name() == "b"); + ASSERT(session1->GetChannel("b") == chan1b); + scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b)); + + SessionDescription* desc1 = new SessionDescription(); + ASSERT(session1->state() == Session::STATE_INIT); + bool valid = session1->Initiate("bar@baz.com", NULL, desc1); + ASSERT(valid); + handler1->PrepareTransport(); + + signaling_thread->ProcessMessages(100); + + ASSERT(handler1->last_state == Session::STATE_SENTINITIATE); + scoped_ptr<XmlElement> stanza1, stanza2; + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"initiate\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<ses:description xmlns:ses=\"http://oink.splat/session\"/>" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>" + "</session>" + "</cli:iq>")); + stanza2.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\"" + " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\"" + " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\"" + " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\"" + " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\"" + " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\"" + " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\"" + " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\"" + " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\"" + " network=\"network\"/>" + "</p:transport>" + "</session>" + "</cli:iq>")); + manhandler1->CheckNoStanza(); + + // If the first client were old, the initiate would have no transports and + // the candidates would be sent in a candidates message. + if (old1) { + stanza1.reset(XmlElement::ForStr( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"initiate\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<ses:description xmlns:ses=\"http://oink.splat/session\"/>" + "</session>" + "</cli:iq>")); + stanza2.reset(XmlElement::ForStr( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"candidates\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\"" + " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\"" + " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\"" + " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\"" + " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\"" + " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\"" + " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\"" + " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\"" + " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\"" + " network=\"network\"/>" + "</session>" + "</cli:iq>")); + } + + scoped_ptr<SessionManager> manager2( + new SessionManager(allocator.get(), worker_thread.get())); + scoped_ptr<SessionManagerHandler> manhandler2( + new SessionManagerHandler(manager2.get(), "bar@baz.com")); + client->AddManager(manager2.get()); + + // Deliver the initiate. + manager2->OnIncomingMessage(stanza1.get()); + stanza1.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + + // If client1 is old, we will not see a transport-accept. If client2 is old, + // then we should act as if it did not send one. + if (!old1) { + stanza1.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\"" + " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>" + "</session>" + "</cli:iq>")); + } else { + GetNextID(); // Advance the ID count to be the same in all cases. + stanza1.reset(NULL); + } + if (old2) { + stanza1.reset(NULL); + } + manhandler2->CheckNoStanza(); + ASSERT(manhandler2->create_count == 1); + ASSERT(manhandler2->last_id == session1->id()); + + Session* session2 = manager2->GetSession(session1->id()); + ASSERT(session2); + ASSERT(session1->id() == session2->id()); + ASSERT(manhandler2->last_id == session2->id()); + ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE); + scoped_ptr<SessionHandler> handler2(new SessionHandler(session2)); + handler2->PrepareTransport(); + + ASSERT(session2->name() == session1->remote_name()); + ASSERT(session1->name() == session2->remote_name()); + + ASSERT(session2->transport() != NULL); + ASSERT(session2->transport()->name() == kNsP2pTransport); + + ASSERT(client->create_count == 2); + TransportChannel* chan2a = client->a; + scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a)); + TransportChannel* chan2b = client->b; + scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b)); + + // Deliver the candidates. + manager2->OnIncomingMessage(stanza2.get()); + stanza2.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + + signaling_thread->ProcessMessages(10); + + // If client1 is old, we should see a candidates message instead of a + // transport-info. If client2 is old, we should act as if we did. + const char* kCandidates2 = + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"candidates\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\"" + " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\"" + " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\"" + " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\"" + " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\"" + " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\"" + " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\"" + " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\"" + " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\"" + " network=\"network\"/>" + "</session>" + "</cli:iq>"; + if (old1) { + stanza2.reset(manhandler2->CheckNextStanza(kCandidates2)); + } else { + stanza2.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\"" + " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\"" + " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\"" + " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\"" + " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\"" + " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\"" + " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\"" + " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\"" + " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\"" + " network=\"network\"/>" + "</p:transport>" + "</session>" + "</cli:iq>")); + } + if (old2) { + stanza2.reset(XmlElement::ForStr(kCandidates2)); + } + manhandler2->CheckNoStanza(); + + // Deliver the transport-accept if one exists. + if (stanza1.get() != NULL) { + manager1->OnIncomingMessage(stanza1.get()); + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler1->CheckNoStanza(); + + // The first session should now have a transport. + ASSERT(session1->transport() != NULL); + ASSERT(session1->transport()->name() == kNsP2pTransport); + } + + // Deliver the candidates. If client2 is old (or is acting old because + // client1 is), then client1 will correct its earlier mistake of sending + // transport-info by sending a candidates message. If client1 is supposed to + // be old, then it sent candidates earlier, so we drop this. + manager1->OnIncomingMessage(stanza2.get()); + if (old1 || old2) { + stanza2.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"4\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"candidates\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\"" + " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\"" + " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\"" + " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\"" + " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\"" + " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\"" + " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\"" + " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\"" + " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\"" + " network=\"network\"/>" + "</session>" + "</cli:iq>")); + } else { + GetNextID(); // Advance the ID count to be the same in all cases. + stanza2.reset(NULL); + } + if (old1) { + stanza2.reset(NULL); + } + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler1->CheckNoStanza(); + + // The first session must have a transport in either case now. + ASSERT(session1->transport() != NULL); + ASSERT(session1->transport()->name() == kNsP2pTransport); + + // If client1 just generated a candidates message, then we must deliver it. + if (stanza2.get() != NULL) { + manager2->OnIncomingMessage(stanza2.get()); + stanza2.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" id=\"4\" type=\"result\" from=\"bar@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler2->CheckNoStanza(); + } + + // The channels should be able to become writable at this point. This + // requires pinging, so it may take a little while. + signaling_thread->ProcessMessages(500); + ASSERT(chan1a->writable() && chan1a->readable()); + ASSERT(chan1b->writable() && chan1b->readable()); + ASSERT(chan2a->writable() && chan2a->readable()); + ASSERT(chan2b->writable() && chan2b->readable()); + ASSERT(chanhandler1a->last_writable); + ASSERT(chanhandler1b->last_writable); + ASSERT(chanhandler2a->last_writable); + ASSERT(chanhandler2b->last_writable); + + // Accept the session. + TestAccept(signaling_thread, session1, session2, + handler1.get(), handler2.get(), + manager1.get(), manager2.get(), + manhandler1.get(), manhandler2.get()); + + // Send a bunch of data between them. + TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(), + chanhandler2b.get(), signaling_thread, false); + + manager1->DestroySession(session1); + manager2->DestroySession(session2); + + ASSERT(manhandler1->create_count == 1); + ASSERT(manhandler2->create_count == 1); + ASSERT(manhandler1->destroy_count == 1); + ASSERT(manhandler2->destroy_count == 1); + + worker_thread->Stop(); + + std::cout << "P2P Compatibility: " << test_name << ": PASS" << std::endl; +} + +// Tests the P2P transport. The flags indicate whether they clients will +// advertise support for raw as well. +void TestP2P(const std::string& test_name, bool raw1, bool raw2) { + InitTest(); + + talk_base::Thread* signaling_thread = talk_base::Thread::Current(); + scoped_ptr<talk_base::Thread> worker_thread(new talk_base::Thread()); + worker_thread->Start(); + + scoped_ptr<PortAllocator> allocator( + new TestPortAllocator(worker_thread.get(), NULL)); + scoped_ptr<MySessionClient> client1(new MySessionClient()); + client1->SetTransports(true, raw1); + scoped_ptr<MySessionClient> client2(new MySessionClient()); + client2->SetTransports(true, raw2); + + scoped_ptr<SessionManager> manager1( + new SessionManager(allocator.get(), worker_thread.get())); + scoped_ptr<SessionManagerHandler> manhandler1( + new SessionManagerHandler(manager1.get(), "foo@baz.com")); + client1->AddManager(manager1.get()); + + Session* session1 = manager1->CreateSession("foo@baz.com", kSessionType); + ASSERT(manhandler1->create_count == 1); + ASSERT(manhandler1->last_id == session1->id()); + scoped_ptr<SessionHandler> handler1(new SessionHandler(session1)); + + ASSERT(client1->create_count == 1); + TransportChannel* chan1a = client1->a; + ASSERT(chan1a->name() == "a"); + ASSERT(session1->GetChannel("a") == chan1a); + scoped_ptr<ChannelHandler> chanhandler1a(new ChannelHandler(chan1a)); + TransportChannel* chan1b = client1->b; + ASSERT(chan1b->name() == "b"); + ASSERT(session1->GetChannel("b") == chan1b); + scoped_ptr<ChannelHandler> chanhandler1b(new ChannelHandler(chan1b)); + + SessionDescription* desc1 = new SessionDescription(); + ASSERT(session1->state() == Session::STATE_INIT); + bool valid = session1->Initiate("bar@baz.com", NULL, desc1); + ASSERT(valid); + handler1->PrepareTransport(); + + signaling_thread->ProcessMessages(100); + + ASSERT(handler1->last_state == Session::STATE_SENTINITIATE); + scoped_ptr<XmlElement> stanza1, stanza2; + if (raw1) { + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"initiate\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<ses:description xmlns:ses=\"http://oink.splat/session\"/>" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>" + "<raw:transport xmlns:raw=\"http://www.google.com/transport/raw\"/>" + "</session>" + "</cli:iq>")); + } else { + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"0\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"initiate\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<ses:description xmlns:ses=\"http://oink.splat/session\"/>" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>" + "</session>" + "</cli:iq>")); + } + stanza2.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" type=\"set\" from=\"foo@baz.com\" id=\"1\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28653\"" + " preference=\"1\" username=\"h0ISP4S5SJKH/9EY\" protocol=\"udp\"" + " generation=\"0\" password=\"UhnAmO5C89dD2dZ+\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28658\"" + " preference=\"1\" username=\"yid4vfB3zXPvrRB9\" protocol=\"udp\"" + " generation=\"0\" password=\"SqLXTvcEyriIo+Mj\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28663\"" + " preference=\"1\" username=\"NvT78D7WxPWM1KL8\" protocol=\"udp\"" + " generation=\"0\" password=\"+mV/QhOapXu4caPX\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28668\"" + " preference=\"1\" username=\"8EzB7MH+TYpIlSp/\" protocol=\"udp\"" + " generation=\"0\" password=\"h+MelLXupoK5aYqC\" type=\"local\"" + " network=\"network\"/>" + "</p:transport>" + "</session>" + "</cli:iq>")); + manhandler1->CheckNoStanza(); + + scoped_ptr<SessionManager> manager2( + new SessionManager(allocator.get(), worker_thread.get())); + scoped_ptr<SessionManagerHandler> manhandler2( + new SessionManagerHandler(manager2.get(), "bar@baz.com")); + client2->AddManager(manager2.get()); + + // Deliver the initiate. + manager2->OnIncomingMessage(stanza1.get()); + stanza1.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" id=\"0\" type=\"result\" from=\"bar@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + stanza1.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"2\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\"" + " type=\"transport-accept\" id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\"/>" + "</session>" + "</cli:iq>")); + manhandler2->CheckNoStanza(); + ASSERT(manhandler2->create_count == 1); + ASSERT(manhandler2->last_id == session1->id()); + + Session* session2 = manager2->GetSession(session1->id()); + ASSERT(session2); + ASSERT(session1->id() == session2->id()); + ASSERT(manhandler2->last_id == session2->id()); + ASSERT(session2->state() == Session::STATE_RECEIVEDINITIATE); + scoped_ptr<SessionHandler> handler2(new SessionHandler(session2)); + handler2->PrepareTransport(); + + ASSERT(session2->name() == session1->remote_name()); + ASSERT(session1->name() == session2->remote_name()); + + ASSERT(session2->transport() != NULL); + ASSERT(session2->transport()->name() == kNsP2pTransport); + + ASSERT(client2->create_count == 1); + TransportChannel* chan2a = client2->a; + scoped_ptr<ChannelHandler> chanhandler2a(new ChannelHandler(chan2a)); + TransportChannel* chan2b = client2->b; + scoped_ptr<ChannelHandler> chanhandler2b(new ChannelHandler(chan2b)); + + // Deliver the candidates. + manager2->OnIncomingMessage(stanza2.get()); + stanza2.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" id=\"1\" type=\"result\" from=\"bar@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + + signaling_thread->ProcessMessages(10); + + stanza2.reset(manhandler2->CheckNextStanza( + "<cli:iq to=\"foo@baz.com\" type=\"set\" from=\"bar@baz.com\" id=\"3\"" + " xmlns:cli=\"jabber:client\">" + "<session xmlns=\"http://www.google.com/session\" type=\"transport-info\"" + " id=\"2154761789\" initiator=\"foo@baz.com\">" + "<p:transport xmlns:p=\"http://www.google.com/transport/p2p\">" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28673\"" + " preference=\"1\" username=\"FJDz3iuXjbQJDRjs\" protocol=\"udp\"" + " generation=\"0\" password=\"Ca5daV9m6G91qhlM\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"a\" address=\"127.0.0.1\" port=\"28678\"" + " preference=\"1\" username=\"xlN53r3Jn/R5XuCt\" protocol=\"udp\"" + " generation=\"0\" password=\"rgik2pKsjaPSUdJd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28683\"" + " preference=\"1\" username=\"IBZ8CSq8ot2+pSMp\" protocol=\"udp\"" + " generation=\"0\" password=\"i7RcDsGntMI6fzdd\" type=\"local\"" + " network=\"network\"/>" + "<candidate name=\"b\" address=\"127.0.0.1\" port=\"28688\"" + " preference=\"1\" username=\"SEtih9PYtMHCAlMI\" protocol=\"udp\"" + " generation=\"0\" password=\"wROrHJ3+gDxUUMp1\" type=\"local\"" + " network=\"network\"/>" + "</p:transport>" + "</session>" + "</cli:iq>")); + manhandler2->CheckNoStanza(); + + // Deliver the transport-accept. + manager1->OnIncomingMessage(stanza1.get()); + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" id=\"2\" type=\"result\" from=\"foo@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler1->CheckNoStanza(); + + // The first session should now have a transport. + ASSERT(session1->transport() != NULL); + ASSERT(session1->transport()->name() == kNsP2pTransport); + + // Deliver the candidates. + manager1->OnIncomingMessage(stanza2.get()); + stanza1.reset(manhandler1->CheckNextStanza( + "<cli:iq to=\"bar@baz.com\" id=\"3\" type=\"result\" from=\"foo@baz.com\"" + " xmlns:cli=\"jabber:client\"/>")); + manhandler1->CheckNoStanza(); + + // The channels should be able to become writable at this point. This + // requires pinging, so it may take a little while. + signaling_thread->ProcessMessages(500); + ASSERT(chan1a->writable() && chan1a->readable()); + ASSERT(chan1b->writable() && chan1b->readable()); + ASSERT(chan2a->writable() && chan2a->readable()); + ASSERT(chan2b->writable() && chan2b->readable()); + ASSERT(chanhandler1a->last_writable); + ASSERT(chanhandler1b->last_writable); + ASSERT(chanhandler2a->last_writable); + ASSERT(chanhandler2b->last_writable); + + // Accept the session. + TestAccept(signaling_thread, session1, session2, + handler1.get(), handler2.get(), + manager1.get(), manager2.get(), + manhandler1.get(), manhandler2.get()); + + // Send a bunch of data between them. + TestSendRecv(chanhandler1a.get(), chanhandler1b.get(), chanhandler2a.get(), + chanhandler2b.get(), signaling_thread, false); + + manager1->DestroySession(session1); + manager2->DestroySession(session2); + + ASSERT(manhandler1->create_count == 1); + ASSERT(manhandler2->create_count == 1); + ASSERT(manhandler1->destroy_count == 1); + ASSERT(manhandler2->destroy_count == 1); + + worker_thread->Stop(); + + std::cout << "P2P: " << test_name << ": PASS" << std::endl; +} +// +int main(int argc, char* argv[]) { + talk_base::LogMessage::LogToDebug(talk_base::LS_WARNING); + + TestP2P("{p2p} => {p2p}", false, false); + TestP2P("{p2p} => {p2p,raw}", false, true); + TestP2P("{p2p,raw} => {p2p}", true, false); + TestP2P("{p2p,raw} => {p2p,raw}", true, true); + TestP2PCompatibility("New => New", false, false); + TestP2PCompatibility("Old => New", true, false); + TestP2PCompatibility("New => Old", false, true); + TestP2PCompatibility("Old => Old", true, true); + + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h new file mode 100644 index 0000000..a92df1d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionclient.h @@ -0,0 +1,71 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_SESSIONCLIENT_H_ +#define _CRICKET_P2P_BASE_SESSIONCLIENT_H_ + +namespace buzz { +class XmlElement; +} + +namespace cricket { + +class Session; +class SessionDescription; + +// A SessionClient exists in 1-1 relation with each session. The implementor +// of this interface is the one that understands *what* the two sides are +// trying to send to one another. The lower-level layers only know how to send +// data; they do not know what is being sent. +class SessionClient { + public: + // Notifies the client of the creation / destruction of sessions of this type. + // + // IMPORTANT: The SessionClient, in its handling of OnSessionCreate, must + // create whatever channels are indicate in the description. This is because + // the remote client may already be attempting to connect those channels. If + // we do not create our channel right away, then connection may fail or be + // delayed. + virtual void OnSessionCreate(Session* session, bool received_initiate) = 0; + virtual void OnSessionDestroy(Session* session) = 0; + + // Provides functions to convert between the XML description of the session + // and the data structures useful to the client. The resulting objects are + // held by the Session for easy access. + virtual const SessionDescription* CreateSessionDescription( + const buzz::XmlElement* element) = 0; + virtual buzz::XmlElement* TranslateSessionDescription( + const SessionDescription* description) = 0; + +protected: + // The SessionClient interface explicitly does not include destructor + virtual ~SessionClient() { } +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_SESSIONCLIENT_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h b/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h new file mode 100644 index 0000000..28b7084 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessiondescription.h @@ -0,0 +1,42 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SESSIONDESCRIPTION_H_ +#define _SESSIONDESCRIPTION_H_ + +namespace cricket { + +// The client overrides this with whatever + +class SessionDescription { +public: + virtual ~SessionDescription() {} +}; + +} // namespace cricket + +#endif // _SESSIONDESCRIPTION_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h new file mode 100644 index 0000000..a12535c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionid.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SESSIONID_H_ +#define _SESSIONID_H_ + +#include "talk/base/basictypes.h" +#include <string> +#include <sstream> + +namespace cricket { + +// Each session is identified by a pair (from,id), where id is only +// assumed to be unique to the machine identified by from. +class SessionID { +public: + SessionID() : id_str_("0") { + } + SessionID(const std::string& initiator, uint32 id) + : initiator_(initiator) { + set_id(id); + } + SessionID(const SessionID& sid) + : id_str_(sid.id_str_), initiator_(sid.initiator_) { + } + + void set_id(uint32 id) { + std::stringstream st; + st << id; + st >> id_str_; + } + const std::string id_str() const { + return id_str_; + } + void set_id_str(const std::string &id_str) { + id_str_ = id_str; + } + + const std::string &initiator() const { + return initiator_; + } + void set_initiator(const std::string &initiator) { + initiator_ = initiator; + } + + bool operator <(const SessionID& sid) const { + int r = initiator_.compare(sid.initiator_); + if (r == 0) + r = id_str_.compare(sid.id_str_); + return r < 0; + } + + bool operator ==(const SessionID& sid) const { + return (id_str_ == sid.id_str_) && (initiator_ == sid.initiator_); + } + + SessionID& operator =(const SessionID& sid) { + id_str_ = sid.id_str_; + initiator_ = sid.initiator_; + return *this; + } + +private: + std::string id_str_; + std::string initiator_; +}; + +} // namespace cricket + +#endif // _SESSIONID_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc new file mode 100644 index 0000000..5d030bb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.cc @@ -0,0 +1,336 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/sessionmanager.h" +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/constants.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/jid.h" + +namespace cricket { + +SessionManager::SessionManager(PortAllocator *allocator, + talk_base::Thread *worker, + talk_base::Thread *signaling_thread) { + allocator_ = allocator; + if (signaling_thread == NULL) { + signaling_thread_ = talk_base::Thread::Current(); + } else { + signaling_thread_ = signaling_thread; + } + if (worker == NULL) { + worker_thread_ = talk_base::Thread::Current(); + } else { + worker_thread_ = worker; + } + timeout_ = 50; +} + +SessionManager::~SessionManager() { + // Note: Session::Terminate occurs asynchronously, so it's too late to + // delete them now. They better be all gone. + ASSERT(session_map_.empty()); + //TerminateAll(); +} + +void SessionManager::AddClient(const std::string& session_type, + SessionClient* client) { + ASSERT(client_map_.find(session_type) == client_map_.end()); + client_map_[session_type] = client; +} + +void SessionManager::RemoveClient(const std::string& session_type) { + ClientMap::iterator iter = client_map_.find(session_type); + ASSERT(iter != client_map_.end()); + client_map_.erase(iter); +} + +SessionClient* SessionManager::GetClient(const std::string& session_type) { + ClientMap::iterator iter = client_map_.find(session_type); + return (iter != client_map_.end()) ? iter->second : NULL; +} + +Session *SessionManager::CreateSession(const std::string& name, + const std::string& session_type) { + return CreateSession(name, SessionID(name, CreateRandomId()), session_type, + false); +} + +Session *SessionManager::CreateSession( + const std::string &name, const SessionID& id, + const std::string& session_type, bool received_initiate) { + SessionClient* client = GetClient(session_type); + ASSERT(client != NULL); + + Session *session = new Session(this, name, id, session_type, client); + session_map_[session->id()] = session; + session->SignalRequestSignaling.connect( + this, &SessionManager::OnRequestSignaling); + session->SignalOutgoingMessage.connect( + this, &SessionManager::OnOutgoingMessage); + session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); + SignalSessionCreate(session, received_initiate); + session->client()->OnSessionCreate(session, received_initiate); + return session; +} + +void SessionManager::DestroySession(Session *session) { + if (session != NULL) { + SessionMap::iterator it = session_map_.find(session->id()); + if (it != session_map_.end()) { + SignalSessionDestroy(session); + session->client()->OnSessionDestroy(session); + session_map_.erase(it); + delete session; + } + } +} + +Session *SessionManager::GetSession(const SessionID& id) { + SessionMap::iterator it = session_map_.find(id); + if (it != session_map_.end()) + return it->second; + return NULL; +} + +void SessionManager::TerminateAll() { + while (session_map_.begin() != session_map_.end()) { + Session *session = session_map_.begin()->second; + session->Terminate(); + } +} + +bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { + if (stanza->Name() != buzz::QN_IQ) + return false; + if (!stanza->HasAttr(buzz::QN_TYPE)) + return false; + if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET) + return false; + + const buzz::XmlElement* session = stanza->FirstNamed(QN_SESSION); + if (!session) + return false; + if (!session->HasAttr(buzz::QN_TYPE)) + return false; + if (!session->HasAttr(buzz::QN_ID) || !session->HasAttr(QN_INITIATOR)) + return false; + + return true; +} + +Session* SessionManager::FindSessionForStanza(const buzz::XmlElement* stanza, + bool incoming) { + const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION); + ASSERT(session_xml != NULL); + + SessionID id; + id.set_id_str(session_xml->Attr(buzz::QN_ID)); + id.set_initiator(session_xml->Attr(QN_INITIATOR)); + + // Pass this message to the session in question. + SessionMap::iterator iter = session_map_.find(id); + if (iter == session_map_.end()) + return NULL; + + Session* session = iter->second; + + // match on "from"? or "to"? + buzz::QName attr = buzz::QN_TO; + if (incoming) { + attr = buzz::QN_FROM; + } + buzz::Jid remote(session->remote_name()); + buzz::Jid match(stanza->Attr(attr)); + if (remote == match) { + return session; + } + return NULL; +} + +void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { + ASSERT(stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET); + + Session* session = FindSessionForStanza(stanza, true); + if (session) { + session->OnIncomingMessage(stanza); + return; + } + + const buzz::XmlElement* session_xml = stanza->FirstNamed(QN_SESSION); + ASSERT(session_xml != NULL); + if (session_xml->Attr(buzz::QN_TYPE) == "initiate") { + std::string session_type = FindClient(session_xml); + if (session_type.size() == 0) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session description type", NULL); + } else { + SessionID id; + id.set_id_str(session_xml->Attr(buzz::QN_ID)); + id.set_initiator(session_xml->Attr(QN_INITIATOR)); + + session = CreateSession(stanza->Attr(buzz::QN_TO), + id, + session_type, true); + session->OnIncomingMessage(stanza); + + // If we haven't rejected, and we haven't selected a transport yet, + // let's do it now. + if ((session->state() != Session::STATE_SENTREJECT) && + (session->transport() == NULL)) { + session->ChooseTransport(stanza); + } + } + return; + } + + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session", NULL); +} + +void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza) { + // We don't do anything with the response now. If we need to we can forward + // it to the session. + return; +} + +void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza) { + Session* session = FindSessionForStanza(orig_stanza, false); + if (session) { + scoped_ptr<buzz::XmlElement> synthetic_error; + if (!error_stanza) { + // A failed send is semantically equivalent to an error response, so we + // can just turn the former into the latter. + synthetic_error.reset( + CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, + "cancel", "Recipient did not respond", NULL)); + error_stanza = synthetic_error.get(); + } + + session->OnFailedSend(orig_stanza, error_stanza); + } +} + +std::string SessionManager::FindClient(const buzz::XmlElement* session) { + for (const buzz::XmlElement* elem = session->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + if (elem->Name().LocalPart() == "description") { + ClientMap::iterator iter = client_map_.find(elem->Name().Namespace()); + if (iter != client_map_.end()) + return iter->first; + } + } + return ""; +} + +void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + scoped_ptr<buzz::XmlElement> msg( + CreateErrorMessage(stanza, name, type, text, extra_info)); + SignalOutgoingMessage(msg.get()); +} + +buzz::XmlElement* SessionManager::CreateErrorMessage( + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); + iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); + iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); + iq->SetAttr(buzz::QN_TYPE, "error"); + + for (const buzz::XmlElement* elem = stanza->FirstElement(); + elem != NULL; + elem = elem->NextElement()) { + iq->AddElement(new buzz::XmlElement(*elem)); + } + + buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); + error->SetAttr(buzz::QN_TYPE, type); + iq->AddElement(error); + + // If the error name is not in the standard namespace, we have to first add + // some error from that namespace. + if (name.Namespace() != buzz::NS_STANZA) { + error->AddElement( + new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); + } + error->AddElement(new buzz::XmlElement(name)); + + if (extra_info) + error->AddElement(new buzz::XmlElement(*extra_info)); + + if (text.size() > 0) { + // It's okay to always use English here. This text is for debugging + // purposes only. + buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); + text_elem->SetAttr(buzz::QN_XML_LANG, "en"); + text_elem->SetBodyText(text); + error->AddElement(text_elem); + } + + // TODO: Should we include error codes as well for SIP compatibility? + + return iq; +} + +void SessionManager::OnOutgoingMessage(Session* session, + const buzz::XmlElement* stanza) { + SignalOutgoingMessage(stanza); +} + +void SessionManager::OnErrorMessage(Session* session, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + SendErrorMessage(stanza, name, type, text, extra_info); +} + +void SessionManager::OnSignalingReady() { + for (SessionMap::iterator it = session_map_.begin(); + it != session_map_.end(); + ++it) { + it->second->OnSignalingReady(); + } +} + +void SessionManager::OnRequestSignaling(Session* session) { + SignalRequestSignaling(); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h new file mode 100644 index 0000000..e0691cf --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/sessionmanager.h @@ -0,0 +1,182 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SESSIONMANAGER_H_ +#define _SESSIONMANAGER_H_ + +#include "talk/base/thread.h" +#include "talk/p2p/base/portallocator.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessionid.h" +#include "talk/base/sigslot.h" + +#include <string> +#include <utility> +#include <map> + +namespace buzz { +class QName; +class XmlElement; +} + +namespace cricket { + +class Session; +class SessionClient; + +// SessionManager manages session instances + +class SessionManager : public sigslot::has_slots<> { + public: + SessionManager(PortAllocator *allocator, + talk_base::Thread *worker_thread = NULL, + talk_base::Thread *signaling_thread = NULL); + virtual ~SessionManager(); + + PortAllocator *port_allocator() const { return allocator_; } + talk_base::Thread *worker_thread() const { return worker_thread_; } + talk_base::Thread *signaling_thread() const { return signaling_thread_; } + + int session_timeout() const { return timeout_; } + void set_session_timeout(int timeout) { timeout_ = timeout; } + + // Registers support for the given client. If we receive an initiate + // describing a session of the given type, we will automatically create a + // Session object and notify this client. The client may then accept or + // reject the session. + void AddClient(const std::string& session_type, SessionClient* client); + void RemoveClient(const std::string& session_type); + SessionClient* GetClient(const std::string& session_type); + + // Creates a new session. The given name is the JID of the client on whose + // behalf we initiate the session. + Session *CreateSession(const std::string& name, + const std::string& session_type); + + // Destroys the given session. + void DestroySession(Session *session); + + // Returns the session with the given ID or NULL if none exists. + Session *GetSession(const SessionID& id); + + // Terminates all of the sessions created by this manager. + void TerminateAll(); + + // These are signaled whenever the set of existing sessions changes. + sigslot::signal2<Session *, bool> SignalSessionCreate; + sigslot::signal1<Session *> SignalSessionDestroy; + + // Determines whether the given stanza is intended for some session. + bool IsSessionMessage(const buzz::XmlElement* stanza); + + // Given a stanza, this find the Session associated with that stanza + Session* FindSessionForStanza(const buzz::XmlElement* stanza, bool incoming); + + // Called when we receive a stanza for which IsSessionMessage is true. + void OnIncomingMessage(const buzz::XmlElement* stanza); + + // Called when we get a response to a message that we sent. + void OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza); + + // Called if an attempted to send times out or an error is returned. In the + // timeout case error_stanza will be NULL + void OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza); + + // Signalled each time a session generates a signaling message to send. + sigslot::signal1<const buzz::XmlElement*> SignalOutgoingMessage; + + // Signaled before sessions try to send certain signaling messages. The + // client should call OnSignalingReady once it is safe to send them. These + // steps are taken so that we don't send signaling messages trying to + // re-establish the connectivity of a session when the client cannot send + // the messages (and would probably just drop them on the floor). + // + // Note: you can connect this directly to OnSignalingReady(), if a signalling + // check is not supported. + sigslot::signal0<> SignalRequestSignaling; + void OnSignalingReady(); + + private: + typedef std::map<SessionID, Session *> SessionMap; + typedef std::map<std::string, SessionClient*> ClientMap; + + PortAllocator *allocator_; + talk_base::Thread *signaling_thread_; + talk_base::Thread *worker_thread_; + int timeout_; + SessionMap session_map_; + ClientMap client_map_; + + // Helper function for CreateSession. This is also invoked when we receive + // a message attempting to initiate a session with this client. + Session *CreateSession(const std::string& name, + const SessionID& id, + const std::string& session_type, + bool received_initiate); + + // Attempts to find a registered session type whose description appears as + // a child of the session element. Such a child should be present indicating + // the application they hope to initiate. + std::string FindClient(const buzz::XmlElement* session); + + // Sends a message back to the other client indicating that we found an error + // in the stanza they sent. name identifies the error, type is one of the + // standard XMPP types (cancel, continue, modify, auth, wait), and text is a + // description for debugging purposes. + void SendErrorMessage(const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + // Creates and returns an error message from the given components. The + // caller is responsible for deleting this. + buzz::XmlElement* SessionManager::CreateErrorMessage( + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + // Called each time a session requests signaling. + void OnRequestSignaling(Session* session); + + // Called each time a session has an outgoing message. + void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza); + + // Called each time a session has an error to send. + void OnErrorMessage(Session* session, const buzz::XmlElement* stanza, + const buzz::QName& name, const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); +}; + +} // namespace cricket + +#endif // _SESSIONMANAGER_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stun.cc b/Plugins/jingle/libjingle/talk/p2p/base/stun.cc new file mode 100644 index 0000000..c4a89e8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stun.cc @@ -0,0 +1,578 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/logging.h" +#include "talk/p2p/base/stun.h" +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::memcpy; +} +#endif + +using talk_base::ByteBuffer; + +namespace cricket { + +const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST"; +const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED"; +const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE"; +const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS"; +const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE"; +const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME"; +const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS"; +const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR"; +const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE"; + +StunMessage::StunMessage() : type_(0), length_(0), + transaction_id_("0000000000000000") { + assert(transaction_id_.size() == 16); + attrs_ = new std::vector<StunAttribute*>(); +} + +StunMessage::~StunMessage() { + for (unsigned i = 0; i < attrs_->size(); i++) + delete (*attrs_)[i]; + delete attrs_; +} + +void StunMessage::SetTransactionID(const std::string& str) { + assert(str.size() == 16); + transaction_id_ = str; +} + +void StunMessage::AddAttribute(StunAttribute* attr) { + attrs_->push_back(attr); + length_ += attr->length() + 4; +} + +const StunAddressAttribute* +StunMessage::GetAddress(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunUInt32Attribute* +StunMessage::GetUInt32(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunByteStringAttribute* +StunMessage::GetByteString(StunAttributeType type) const { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return reinterpret_cast<const StunByteStringAttribute*>(GetAttribute(type)); + + default: + assert(0); + return 0; + } +} + +const StunErrorCodeAttribute* StunMessage::GetErrorCode() const { + return reinterpret_cast<const StunErrorCodeAttribute*>( + GetAttribute(STUN_ATTR_ERROR_CODE)); +} + +const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const { + return reinterpret_cast<const StunUInt16ListAttribute*>( + GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES)); +} + +const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const { + return reinterpret_cast<const StunTransportPrefsAttribute*>( + GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES)); +} + +const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const { + for (unsigned i = 0; i < attrs_->size(); i++) { + if ((*attrs_)[i]->type() == type) + return (*attrs_)[i]; + } + return 0; +} + +bool StunMessage::Read(ByteBuffer* buf) { + if (!buf->ReadUInt16(type_)) + return false; + + if (!buf->ReadUInt16(length_)) + return false; + + std::string transaction_id; + if (!buf->ReadString(transaction_id, 16)) + return false; + assert(transaction_id.size() == 16); + transaction_id_ = transaction_id; + + if (length_ > buf->Length()) + return false; + + attrs_->resize(0); + + size_t rest = buf->Length() - length_; + while (buf->Length() > rest) { + uint16 attr_type, attr_length; + if (!buf->ReadUInt16(attr_type)) + return false; + if (!buf->ReadUInt16(attr_length)) + return false; + + StunAttribute* attr = StunAttribute::Create(attr_type, attr_length); + if (!attr || !attr->Read(buf)) + return false; + + attrs_->push_back(attr); + } + + if (buf->Length() != rest) { + // fixme: shouldn't be doing this + LOG(LERROR) << "wrong message length" + << " (" << (int)rest << " != " << (int)buf->Length() << ")"; + return false; + } + + return true; +} + +void StunMessage::Write(ByteBuffer* buf) const { + buf->WriteUInt16(type_); + buf->WriteUInt16(length_); + buf->WriteString(transaction_id_); + + for (unsigned i = 0; i < attrs_->size(); i++) { + buf->WriteUInt16((*attrs_)[i]->type()); + buf->WriteUInt16((*attrs_)[i]->length()); + (*attrs_)[i]->Write(buf); + } +} + +StunAttribute::StunAttribute(uint16 type, uint16 length) + : type_(type), length_(length) { +} + +StunAttribute* StunAttribute::Create(uint16 type, uint16 length) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + if (length != StunAddressAttribute::SIZE) + return 0; + return new StunAddressAttribute(type); + + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + if (length != StunUInt32Attribute::SIZE) + return 0; + return new StunUInt32Attribute(type); + + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MAGIC_COOKIE: + return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_MESSAGE_INTEGRITY: + return (length == 20) ? new StunByteStringAttribute(type, length) : 0; + + case STUN_ATTR_DATA: + return new StunByteStringAttribute(type, length); + + case STUN_ATTR_ERROR_CODE: + if (length < StunErrorCodeAttribute::MIN_SIZE) + return 0; + return new StunErrorCodeAttribute(type, length); + + case STUN_ATTR_UNKNOWN_ATTRIBUTES: + return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0; + + case STUN_ATTR_TRANSPORT_PREFERENCES: + if ((length != StunTransportPrefsAttribute::SIZE1) && + (length != StunTransportPrefsAttribute::SIZE2)) + return 0; + return new StunTransportPrefsAttribute(type, length); + + default: + return 0; + } +} + +StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) { + switch (type) { + case STUN_ATTR_MAPPED_ADDRESS: + case STUN_ATTR_RESPONSE_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS: + case STUN_ATTR_CHANGED_ADDRESS: + case STUN_ATTR_REFLECTED_FROM: + case STUN_ATTR_ALTERNATE_SERVER: + case STUN_ATTR_DESTINATION_ADDRESS: + case STUN_ATTR_SOURCE_ADDRESS2: + return new StunAddressAttribute(type); + + default: + assert(false); + return 0; + } +} + +StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) { + switch (type) { + case STUN_ATTR_CHANGE_REQUEST: + case STUN_ATTR_LIFETIME: + case STUN_ATTR_BANDWIDTH: + case STUN_ATTR_OPTIONS: + return new StunUInt32Attribute(type); + + default: + assert(false); + return 0; + } +} + +StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) { + switch (type) { + case STUN_ATTR_USERNAME: + case STUN_ATTR_PASSWORD: + case STUN_ATTR_MESSAGE_INTEGRITY: + case STUN_ATTR_DATA: + case STUN_ATTR_MAGIC_COOKIE: + return new StunByteStringAttribute(type, 0); + + default: + assert(false); + return 0; + } +} + +StunErrorCodeAttribute* StunAttribute::CreateErrorCode() { + return new StunErrorCodeAttribute( + STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE); +} + +StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() { + return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0); +} + +StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() { + return new StunTransportPrefsAttribute( + STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1); +} + +StunAddressAttribute::StunAddressAttribute(uint16 type) + : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) { +} + +bool StunAddressAttribute::Read(ByteBuffer* buf) { + uint8 dummy; + if (!buf->ReadUInt8(dummy)) + return false; + if (!buf->ReadUInt8(family_)) + return false; + if (!buf->ReadUInt16(port_)) + return false; + if (!buf->ReadUInt32(ip_)) + return false; + return true; +} + +void StunAddressAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt8(0); + buf->WriteUInt8(family_); + buf->WriteUInt16(port_); + buf->WriteUInt32(ip_); +} + +StunUInt32Attribute::StunUInt32Attribute(uint16 type) + : StunAttribute(type, SIZE), bits_(0) { +} + +bool StunUInt32Attribute::GetBit(int index) const { + assert((0 <= index) && (index < 32)); + return static_cast<bool>((bits_ >> index) & 0x1); +} + +void StunUInt32Attribute::SetBit(int index, bool value) { + assert((0 <= index) && (index < 32)); + bits_ &= ~(1 << index); + bits_ |= value ? (1 << index) : 0; +} + +bool StunUInt32Attribute::Read(ByteBuffer* buf) { + if (!buf->ReadUInt32(bits_)) + return false; + return true; +} + +void StunUInt32Attribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(bits_); +} + +StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), bytes_(0) { +} + +StunByteStringAttribute::~StunByteStringAttribute() { + delete [] bytes_; +} + +void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) { + delete [] bytes_; + bytes_ = bytes; + SetLength(length); +} + +void StunByteStringAttribute::CopyBytes(const char* bytes) { + CopyBytes(bytes, (uint16)strlen(bytes)); +} + +void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) { + char* new_bytes = new char[length]; + std::memcpy(new_bytes, bytes, length); + SetBytes(new_bytes, length); +} + +uint8 StunByteStringAttribute::GetByte(int index) const { + assert(bytes_); + assert((0 <= index) && (index < length())); + return static_cast<uint8>(bytes_[index]); +} + +void StunByteStringAttribute::SetByte(int index, uint8 value) { + assert(bytes_); + assert((0 <= index) && (index < length())); + bytes_[index] = value; +} + +bool StunByteStringAttribute::Read(ByteBuffer* buf) { + bytes_ = new char[length()]; + if (!buf->ReadBytes(bytes_, length())) + return false; + return true; +} + +void StunByteStringAttribute::Write(ByteBuffer* buf) const { + buf->WriteBytes(bytes_, length()); +} + +StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length) + : StunAttribute(type, length), class_(0), number_(0) { +} + +StunErrorCodeAttribute::~StunErrorCodeAttribute() { +} + +void StunErrorCodeAttribute::SetErrorCode(uint32 code) { + class_ = (uint8)((code >> 8) & 0x7); + number_ = (uint8)(code & 0xff); +} + +void StunErrorCodeAttribute::SetReason(const std::string& reason) { + SetLength(MIN_SIZE + (uint16)reason.size()); + reason_ = reason; +} + +bool StunErrorCodeAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 11) != 0) + LOG(LERROR) << "error-code bits not zero"; + + SetErrorCode(val); + + if (!buf->ReadString(reason_, length() - 4)) + return false; + + return true; +} + +void StunErrorCodeAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32(error_code()); + buf->WriteString(reason_); +} + +StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length) + : StunAttribute(type, length) { + attr_types_ = new std::vector<uint16>(); +} + +StunUInt16ListAttribute::~StunUInt16ListAttribute() { + delete attr_types_; +} + +size_t StunUInt16ListAttribute::Size() const { + return attr_types_->size(); +} + +uint16 StunUInt16ListAttribute::GetType(int index) const { + return (*attr_types_)[index]; +} + +void StunUInt16ListAttribute::SetType(int index, uint16 value) { + (*attr_types_)[index] = value; +} + +void StunUInt16ListAttribute::AddType(uint16 value) { + attr_types_->push_back(value); + SetLength((uint16)attr_types_->size() * 2); +} + +bool StunUInt16ListAttribute::Read(ByteBuffer* buf) { + for (int i = 0; i < length() / 2; i++) { + uint16 attr; + if (!buf->ReadUInt16(attr)) + return false; + attr_types_->push_back(attr); + } + return true; +} + +void StunUInt16ListAttribute::Write(ByteBuffer* buf) const { + for (unsigned i = 0; i < attr_types_->size(); i++) + buf->WriteUInt16((*attr_types_)[i]); +} + +StunTransportPrefsAttribute::StunTransportPrefsAttribute( + uint16 type, uint16 length) + : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) { +} + +StunTransportPrefsAttribute::~StunTransportPrefsAttribute() { + delete addr_; +} + +void StunTransportPrefsAttribute::SetPreallocateAddress( + StunAddressAttribute* addr) { + if (!addr) { + preallocate_ = false; + addr_ = 0; + SetLength(SIZE1); + } else { + preallocate_ = true; + addr_ = addr; + SetLength(SIZE2); + } +} + +bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) { + uint32 val; + if (!buf->ReadUInt32(val)) + return false; + + if ((val >> 3) != 0) + LOG(LERROR) << "transport-preferences bits not zero"; + + preallocate_ = static_cast<bool>((val >> 2) & 0x1); + prefs_ = (uint8)(val & 0x3); + + if (preallocate_ && (prefs_ == 3)) + LOG(LERROR) << "transport-preferences imcompatible P and Typ"; + + if (!preallocate_) { + if (length() != StunUInt32Attribute::SIZE) + return false; + } else { + if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE) + return false; + + addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS); + addr_->Read(buf); + } + + return true; +} + +void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const { + buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_); + + if (preallocate_) + addr_->Write(buf); +} + +StunMessageType GetStunResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_RESPONSE; + default: + return STUN_BINDING_RESPONSE; + } +} + +StunMessageType GetStunErrorResponseType(StunMessageType request_type) { + switch (request_type) { + case STUN_SHARED_SECRET_REQUEST: + return STUN_SHARED_SECRET_ERROR_RESPONSE; + case STUN_ALLOCATE_REQUEST: + return STUN_ALLOCATE_ERROR_RESPONSE; + case STUN_SEND_REQUEST: + return STUN_SEND_ERROR_RESPONSE; + default: + return STUN_BINDING_ERROR_RESPONSE; + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stun.h b/Plugins/jingle/libjingle/talk/p2p/base/stun.h new file mode 100644 index 0000000..4c0459b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stun.h @@ -0,0 +1,364 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __STUN_H__ +#define __STUN_H__ + +// This file contains classes for dealing with the STUN and TURN protocols. +// Both protocols use the same wire format. + +#include "talk/base/basictypes.h" +#include "talk/base/bytebuffer.h" +#include <string> +#include <vector> + +namespace cricket { + +// These are the types of STUN & TURN messages as of last check. +enum StunMessageType { + STUN_BINDING_REQUEST = 0x0001, + STUN_BINDING_RESPONSE = 0x0101, + STUN_BINDING_ERROR_RESPONSE = 0x0111, + STUN_SHARED_SECRET_REQUEST = 0x0002, + STUN_SHARED_SECRET_RESPONSE = 0x0102, + STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112, + STUN_ALLOCATE_REQUEST = 0x0003, + STUN_ALLOCATE_RESPONSE = 0x0103, + STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, + STUN_SEND_REQUEST = 0x0004, + STUN_SEND_RESPONSE = 0x0104, + STUN_SEND_ERROR_RESPONSE = 0x0114, + STUN_DATA_INDICATION = 0x0115 +}; + +// These are the types of attributes defined in STUN & TURN. Next to each is +// the name of the class (T is StunTAttribute) that implements that type. +enum StunAttributeType { + STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address + STUN_ATTR_RESPONSE_ADDRESS = 0x0002, // Address + STUN_ATTR_CHANGE_REQUEST = 0x0003, // UInt32 + STUN_ATTR_SOURCE_ADDRESS = 0x0004, // Address + STUN_ATTR_CHANGED_ADDRESS = 0x0005, // Address + STUN_ATTR_USERNAME = 0x0006, // ByteString, multiple of 4 bytes + STUN_ATTR_PASSWORD = 0x0007, // ByteString, multiple of 4 bytes + STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes + STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode + STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List + STUN_ATTR_REFLECTED_FROM = 0x000b, // Address + STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, // TransportPrefs + STUN_ATTR_LIFETIME = 0x000d, // UInt32 + STUN_ATTR_ALTERNATE_SERVER = 0x000e, // Address + STUN_ATTR_MAGIC_COOKIE = 0x000f, // ByteString, 4 bytes + STUN_ATTR_BANDWIDTH = 0x0010, // UInt32 + STUN_ATTR_DESTINATION_ADDRESS = 0x0011, // Address + STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, // Address + STUN_ATTR_DATA = 0x0013, // ByteString + STUN_ATTR_OPTIONS = 0x8001 // UInt32 +}; + +enum StunErrorCodes { + STUN_ERROR_BAD_REQUEST = 400, + STUN_ERROR_UNAUTHORIZED = 401, + STUN_ERROR_UNKNOWN_ATTRIBUTE = 420, + STUN_ERROR_STALE_CREDENTIALS = 430, + STUN_ERROR_INTEGRITY_CHECK_FAILURE = 431, + STUN_ERROR_MISSING_USERNAME = 432, + STUN_ERROR_USE_TLS = 433, + STUN_ERROR_SERVER_ERROR = 500, + STUN_ERROR_GLOBAL_FAILURE = 600 +}; + +extern const std::string STUN_ERROR_REASON_BAD_REQUEST; +extern const std::string STUN_ERROR_REASON_UNAUTHORIZED; +extern const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE; +extern const std::string STUN_ERROR_REASON_STALE_CREDENTIALS; +extern const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE; +extern const std::string STUN_ERROR_REASON_MISSING_USERNAME; +extern const std::string STUN_ERROR_REASON_USE_TLS; +extern const std::string STUN_ERROR_REASON_SERVER_ERROR; +extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE; + +class StunAttribute; +class StunAddressAttribute; +class StunUInt32Attribute; +class StunByteStringAttribute; +class StunErrorCodeAttribute; +class StunUInt16ListAttribute; +class StunTransportPrefsAttribute; + +// Records a complete STUN/TURN message. Each message consists of a type and +// any number of attributes. Each attribute is parsed into an instance of an +// appropriate class (see above). The Get* methods will return instances of +// that attribute class. +class StunMessage { +public: + StunMessage(); + ~StunMessage(); + + StunMessageType type() const { return static_cast<StunMessageType>(type_); } + uint16 length() const { return length_; } + const std::string& transaction_id() const { return transaction_id_; } + + void SetType(StunMessageType type) { type_ = type; } + void SetTransactionID(const std::string& str); + + const StunAddressAttribute* GetAddress(StunAttributeType type) const; + const StunUInt32Attribute* GetUInt32(StunAttributeType type) const; + const StunByteStringAttribute* GetByteString(StunAttributeType type) const; + const StunErrorCodeAttribute* GetErrorCode() const; + const StunUInt16ListAttribute* GetUnknownAttributes() const; + const StunTransportPrefsAttribute* GetTransportPrefs() const; + + void AddAttribute(StunAttribute* attr); + + // Parses the STUN/TURN packet in the given buffer and records it here. The + // return value indicates whether this was successful. + bool Read(talk_base::ByteBuffer* buf); + + // Writes this object into a STUN/TURN packet. Return value is true if + // successful. + void Write(talk_base::ByteBuffer* buf) const; + +private: + uint16 type_; + uint16 length_; + std::string transaction_id_; + std::vector<StunAttribute*>* attrs_; + + const StunAttribute* GetAttribute(StunAttributeType type) const; +}; + +// Base class for all STUN/TURN attributes. +class StunAttribute { +public: + virtual ~StunAttribute() {} + + StunAttributeType type() const { + return static_cast<StunAttributeType>(type_); + } + uint16 length() const { return length_; } + + // Reads the body (not the type or length) for this type of attribute from + // the given buffer. Return value is true if successful. + virtual bool Read(talk_base::ByteBuffer* buf) = 0; + + // Writes the body (not the type or length) to the given buffer. Return + // value is true if successful. + virtual void Write(talk_base::ByteBuffer* buf) const = 0; + + // Creates an attribute object with the given type and len. + static StunAttribute* Create(uint16 type, uint16 length); + + // Creates an attribute object with the given type and smallest length. + static StunAddressAttribute* CreateAddress(uint16 type); + static StunUInt32Attribute* CreateUInt32(uint16 type); + static StunByteStringAttribute* CreateByteString(uint16 type); + static StunErrorCodeAttribute* CreateErrorCode(); + static StunUInt16ListAttribute* CreateUnknownAttributes(); + static StunTransportPrefsAttribute* CreateTransportPrefs(); + +protected: + StunAttribute(uint16 type, uint16 length); + + void SetLength(uint16 length) { length_ = length; } + +private: + uint16 type_; + uint16 length_; +}; + +// Implements STUN/TURN attributes that record an Internet address. +class StunAddressAttribute : public StunAttribute { +public: + StunAddressAttribute(uint16 type); + +#if (_MSC_VER < 1300) + enum { SIZE = 8 }; +#else + static const uint16 SIZE = 8; +#endif + + uint8 family() const { return family_; } + uint16 port() const { return port_; } + uint32 ip() const { return ip_; } + + void SetFamily(uint8 family) { family_ = family; } + void SetIP(uint32 ip) { ip_ = ip; } + void SetPort(uint16 port) { port_ = port; } + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + uint8 family_; + uint16 port_; + uint32 ip_; +}; + +// Implements STUN/TURN attributs that record a 32-bit integer. +class StunUInt32Attribute : public StunAttribute { +public: + StunUInt32Attribute(uint16 type); + +#if (_MSC_VER < 1300) + enum { SIZE = 4 }; +#else + static const uint16 SIZE = 4; +#endif + + uint32 value() const { return bits_; } + + void SetValue(uint32 bits) { bits_ = bits; } + + bool GetBit(int index) const; + void SetBit(int index, bool value); + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + uint32 bits_; +}; + +// Implements STUN/TURN attributs that record an arbitrary byte string +class StunByteStringAttribute : public StunAttribute { +public: + StunByteStringAttribute(uint16 type, uint16 length); + ~StunByteStringAttribute(); + + const char* bytes() const { return bytes_; } + + void SetBytes(char* bytes, uint16 length); + + void CopyBytes(const char* bytes); // uses strlen + void CopyBytes(const void* bytes, uint16 length); + + uint8 GetByte(int index) const; + void SetByte(int index, uint8 value); + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + char* bytes_; +}; + +// Implements STUN/TURN attributs that record an error code. +class StunErrorCodeAttribute : public StunAttribute { +public: + StunErrorCodeAttribute(uint16 type, uint16 length); + ~StunErrorCodeAttribute(); + +#if (_MSC_VER < 1300) + enum { MIN_SIZE = 4 }; +#else + static const uint16 MIN_SIZE = 4; +#endif + + uint32 error_code() const { return (class_ << 8) | number_; } + uint8 error_class() const { return class_; } + uint8 number() const { return number_; } + const std::string& reason() const { return reason_; } + + void SetErrorCode(uint32 code); + void SetErrorClass(uint8 eclass) { class_ = eclass; } + void SetNumber(uint8 number) { number_ = number; } + void SetReason(const std::string& reason); + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + uint8 class_; + uint8 number_; + std::string reason_; +}; + +// Implements STUN/TURN attributs that record a list of attribute names. +class StunUInt16ListAttribute : public StunAttribute { +public: + StunUInt16ListAttribute(uint16 type, uint16 length); + ~StunUInt16ListAttribute(); + + size_t Size() const; + uint16 GetType(int index) const; + void SetType(int index, uint16 value); + void AddType(uint16 value); + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + std::vector<uint16>* attr_types_; +}; + +// Implements the TURN TRANSPORT-PREFS attribute, which provides information +// about the ports to allocate. +class StunTransportPrefsAttribute : public StunAttribute { +public: + StunTransportPrefsAttribute(uint16 type, uint16 length); + ~StunTransportPrefsAttribute(); + +#if (_MSC_VER < 1300) + enum { SIZE1 = 4, SIZE2 = 12 }; +#else + static const uint16 SIZE1 = 4; + static const uint16 SIZE2 = 12; +#endif + + bool preallocate() const { return preallocate_; } + uint8 preference_type() const { return prefs_; } + const StunAddressAttribute* address() const { return addr_; } + + void SetPreferenceType(uint8 prefs) { prefs_ = prefs; } + + // Sets the preallocate address to the given value, or if 0 is given, it sets + // to not preallocate. + void SetPreallocateAddress(StunAddressAttribute* addr); + + bool Read(talk_base::ByteBuffer* buf); + void Write(talk_base::ByteBuffer* buf) const; + +private: + bool preallocate_; + uint8 prefs_; + StunAddressAttribute* addr_; +}; + +// The special MAGIC-COOKIE attribute is used to distinguish TURN packets from +// other kinds of traffic. +const char STUN_MAGIC_COOKIE_VALUE[] = { 0x72, char(0xc6), 0x4b, char(0xc6) }; + +// Returns the (successful) response type for the given request type. +StunMessageType GetStunResponseType(StunMessageType request_type); + +// Returns the error response type for the given request type. +StunMessageType GetStunErrorResponseType(StunMessageType request_type); + +} // namespace cricket + +#endif // __STUN_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc new file mode 100644 index 0000000..8fa3fd8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunport.cc @@ -0,0 +1,204 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include <iostream> +#include <cassert> +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/stunport.h" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +#include <errno.h> +#endif // POSIX + +namespace cricket { + +const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts +const int RETRY_DELAY = 50; // 50ms, from ICE spec +const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs + +// Handles a binding request sent to the STUN server. +class StunPortBindingRequest : public StunRequest { +public: + StunPortBindingRequest(StunPort* port, bool keep_alive, + const talk_base::SocketAddress& addr) + : port_(port), keep_alive_(keep_alive), server_addr_(addr) { + start_time_ = talk_base::GetMillisecondCount(); + } + + virtual ~StunPortBindingRequest() { + } + + const talk_base::SocketAddress& server_addr() const { return server_addr_; } + + virtual void Prepare(StunMessage* request) { + request->SetType(STUN_BINDING_REQUEST); + } + + virtual void OnResponse(StunMessage* response) { + const StunAddressAttribute* addr_attr = + response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + if (!addr_attr) { + LOG(LERROR) << "Binding response missing mapped address."; + } else if (addr_attr->family() != 1) { + LOG(LERROR) << "Binding address has bad family"; + } else { + talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port()); + port_->AddAddress(addr, "udp", true); + } + + // We will do a keep-alive regardless of whether this request suceeds. + // This should have almost no impact on network usage. + if (keep_alive_) { + port_->requests_.SendDelayed( + new StunPortBindingRequest(port_, true, server_addr_), + KEEPALIVE_DELAY); + } + } + + virtual void OnErrorResponse(StunMessage* response) { + const StunErrorCodeAttribute* attr = response->GetErrorCode(); + if (!attr) { + LOG(LERROR) << "Bad allocate response error code"; + } else { + LOG(LERROR) << "Binding error response:" + << " class=" << attr->error_class() + << " number=" << attr->number() + << " reason='" << attr->reason() << "'"; + } + + port_->SignalAddressError(port_); + + if (keep_alive_ + && (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)) { + port_->requests_.SendDelayed( + new StunPortBindingRequest(port_, true, server_addr_), + KEEPALIVE_DELAY); + } + } + + virtual void OnTimeout() { + LOG(LERROR) << "Binding request timed out from " + << port_->GetLocalAddress().ToString() + << " (" << port_->network()->name() << ")"; + + port_->SignalAddressError(port_); + + if (keep_alive_ + && (talk_base::GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)) { + port_->requests_.SendDelayed( + new StunPortBindingRequest(port_, true, server_addr_), + RETRY_DELAY); + } + } + +private: + StunPort* port_; + bool keep_alive_; + talk_base::SocketAddress server_addr_; + uint32 start_time_; +}; + +const std::string STUN_PORT_TYPE("stun"); + +StunPort::StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, + const talk_base::SocketAddress& local_addr, + const talk_base::SocketAddress& server_addr) + : UDPPort(thread, STUN_PORT_TYPE, factory, network), + server_addr_(server_addr), requests_(thread), error_(0) { + + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); + if (socket_->Bind(local_addr) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; + + requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); +} + +StunPort::~StunPort() { + delete socket_; +} + +void StunPort::PrepareAddress() { + // We will keep pinging the stun server to make sure our NAT pin-hole stays + // open during the call. + requests_.Send(new StunPortBindingRequest(this, true, server_addr_)); +} + +void StunPort::PrepareSecondaryAddress() { + ASSERT(!server_addr2_.IsAny()); + requests_.Send(new StunPortBindingRequest(this, false, server_addr2_)); +} + +int StunPort::SendTo( + const void* data, size_t size, const talk_base::SocketAddress& addr, + bool payload) { + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int StunPort::SetOption(talk_base::Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int StunPort::GetError() { + return error_; +} + +void StunPort::OnReadPacket( + const char* data, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + assert(socket == socket_); + + // Look for a response to a binding request. + if (requests_.CheckResponse(data, size)) + return; + + // Process this data packet in the normal manner. + UDPPort::OnReadPacket(data, size, remote_addr); +} + +void StunPort::OnSendPacket(const void* data, size_t size, StunRequest* req) { + StunPortBindingRequest* sreq = static_cast<StunPortBindingRequest*>(req); + if (socket_->SendTo(data, size, sreq->server_addr()) < 0) + PLOG(LERROR, socket_->GetError()) << "sendto"; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunport.h b/Plugins/jingle/libjingle/talk/p2p/base/stunport.h new file mode 100644 index 0000000..4b62181 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunport.h @@ -0,0 +1,94 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __STUNPORT_H__ +#define __STUNPORT_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/udpport.h" +#include "talk/p2p/base/stunrequest.h" + +namespace cricket { + +extern const std::string STUN_PORT_TYPE; + +// Communicates using the address on the outside of a NAT. +class StunPort : public UDPPort { +public: + StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, + const talk_base::SocketAddress& local_addr, + const talk_base::SocketAddress& server_addr); + virtual ~StunPort(); + + const talk_base::SocketAddress& server_addr() const { return server_addr_; } + void set_server_addr(const talk_base::SocketAddress& addr) + { server_addr_ = addr; } + + const talk_base::SocketAddress& server_addr2() const { return server_addr2_; } + void set_server_addr2(const talk_base::SocketAddress& addr) + { server_addr2_ = addr; } + + virtual void PrepareAddress(); + + // This will contact the secondary server and signal another candidate + // address for this port (which may be the same as the first address). + void PrepareSecondaryAddress(); + + talk_base::SocketAddress GetLocalAddress() const { + if (socket_) + return socket_->GetLocalAddress(); + return talk_base::SocketAddress(); + } + + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + +protected: + virtual int SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload); + + void OnReadPacket( + const char* data, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + +private: + talk_base::AsyncPacketSocket* socket_; + talk_base::SocketAddress server_addr_; + talk_base::SocketAddress server_addr2_; + StunRequestManager requests_; + int error_; + + friend class StunPortBindingRequest; + + // Sends STUN requests to the server. + void OnSendPacket(const void* data, size_t size, StunRequest* req); +}; + +} // namespace cricket + +#endif // __STUNPORT_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc new file mode 100644 index 0000000..66b8ef6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.cc @@ -0,0 +1,198 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/base/helpers.h" +#include "talk/p2p/base/stunrequest.h" +#include <iostream> +#include <cassert> + +namespace cricket { + +const uint32 MSG_STUN_SEND = 1; + +const int MAX_SENDS = 9; +const int DELAY_UNIT = 100; // 100 milliseconds +const int DELAY_MAX_FACTOR = 16; + +StunRequestManager::StunRequestManager(talk_base::Thread* thread) + : thread_(thread) { + } + +StunRequestManager::~StunRequestManager() { + while (requests_.begin() != requests_.end()) { + StunRequest *request = requests_.begin()->second; + requests_.erase(requests_.begin()); + delete request; + } +} + +void StunRequestManager::Send(StunRequest* request) { + SendDelayed(request, 0); +} + +void StunRequestManager::SendDelayed(StunRequest* request, int delay) { + request->set_manager(this); + assert(requests_.find(request->id()) == requests_.end()); + requests_[request->id()] = request; + thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); +} + +void StunRequestManager::Remove(StunRequest* request) { + assert(request->manager() == this); + RequestMap::iterator iter = requests_.find(request->id()); + if (iter != requests_.end()) { + assert(iter->second == request); + requests_.erase(iter); + thread_->Clear(request); + } +} + +void StunRequestManager::Clear() { + std::vector<StunRequest*> requests; + for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) + requests.push_back(i->second); + + for (uint32 i = 0; i < requests.size(); ++i) + Remove(requests[i]); +} + +bool StunRequestManager::CheckResponse(StunMessage* msg) { + RequestMap::iterator iter = requests_.find(msg->transaction_id()); + if (iter == requests_.end()) + return false; + StunRequest* request = iter->second; + if (msg->type() == GetStunResponseType(request->type())) { + request->OnResponse(msg); + } else if (msg->type() == GetStunErrorResponseType(request->type())) { + request->OnErrorResponse(msg); + } else { + LOG(LERROR) << "Received response with wrong type: " << msg->type() + << " (expecting " << GetStunResponseType(request->type()) << ")"; + return false; + } + + delete request; + return true; +} + +bool StunRequestManager::CheckResponse(const char* data, size_t size) { + // Check the appropriate bytes of the stream to see if they match the + // transaction ID of a response we are expecting. + + if (size < 20) + return false; + + std::string id; + id.append(data + 4, 16); + + RequestMap::iterator iter = requests_.find(id); + if (iter == requests_.end()) + return false; + + // Parse the STUN message and continue processing as usual. + + talk_base::ByteBuffer buf(data, size); + StunMessage msg; + if (!msg.Read(&buf)) + return false; + + return CheckResponse(&msg); +} + +StunRequest::StunRequest() + : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0), + timeout_(false), tstamp_(0) { +} + +StunRequest::StunRequest(StunMessage* request) + : manager_(0), id_(request->transaction_id()), msg_(request), + count_(0), timeout_(false) { +} + +StunRequest::~StunRequest() { + assert(manager_ != NULL); + if (manager_) { + manager_->Remove(this); + manager_->thread_->Clear(this); + } + delete msg_; +} + +const StunMessageType StunRequest::type() { + assert(msg_); + return msg_->type(); +} + +void StunRequest::set_manager(StunRequestManager* manager) { + assert(!manager_); + manager_ = manager; +} + +void StunRequest::OnMessage(talk_base::Message* pmsg) { + assert(manager_); + assert(pmsg->message_id == MSG_STUN_SEND); + + if (!msg_) { + msg_ = new StunMessage(); + msg_->SetTransactionID(id_); + Prepare(msg_); + assert(msg_->transaction_id() == id_); + } + + if (timeout_) { + OnTimeout(); + delete this; + return; + } + + tstamp_ = talk_base::GetMillisecondCount(); + + talk_base::ByteBuffer buf; + msg_->Write(&buf); + manager_->SignalSendPacket(buf.Data(), buf.Length(), this); + + int delay = GetNextDelay(); + manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL); +} + +uint32 StunRequest::Elapsed() const { + return (talk_base::GetMillisecondCount() - tstamp_); +} + +int StunRequest::GetNextDelay() { + int delay = DELAY_UNIT * talk_base::_min(1 << count_, DELAY_MAX_FACTOR); + count_ += 1; + if (count_ == MAX_SENDS) + timeout_ = true; + return delay; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h new file mode 100644 index 0000000..b36861c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunrequest.h @@ -0,0 +1,126 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __STUNREQUESTMANAGER_H__ +#define __STUNREQUESTMANAGER_H__ + +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/stun.h" +#include <map> +#include <string> + +namespace cricket { + +class StunRequest; + +// Manages a set of STUN requests, sending and resending until we receive a +// response or determine that the request has timed out. +class StunRequestManager { +public: + StunRequestManager(talk_base::Thread* thread); + ~StunRequestManager(); + + // Starts sending the given request (perhaps after a delay). + void Send(StunRequest* request); + void SendDelayed(StunRequest* request, int delay); + + // Removes a stun request that was added previously. This will happen + // automatically when a request succeeds, fails, or times out. + void Remove(StunRequest* request); + + // Removes all stun requests that were added previously. + void Clear(); + + // Determines whether the given message is a response to one of the + // outstanding requests, and if so, processes it appropriately. + bool CheckResponse(StunMessage* msg); + bool CheckResponse(const char* data, size_t size); + + // Raised when there are bytes to be sent. + sigslot::signal3<const void*, size_t, StunRequest*> SignalSendPacket; + +private: + typedef std::map<std::string, StunRequest*> RequestMap; + + talk_base::Thread* thread_; + RequestMap requests_; + + friend class StunRequest; +}; + +// Represents an individual request to be sent. The STUN message can either be +// constructed beforehand or built on demand. +class StunRequest : public talk_base::MessageHandler { +public: + StunRequest(); + StunRequest(StunMessage* request); + virtual ~StunRequest(); + + // The manager handling this request (if it has been scheduled for sending). + StunRequestManager* manager() { return manager_; } + + // Returns the transaction ID of this request. + const std::string& id() { return id_; } + + // Returns the STUN type of the request message. + const StunMessageType type(); + + // Handles messages for sending and timeout. + void OnMessage(talk_base::Message* pmsg); + + // Time elapsed since last send (in ms) + uint32 Elapsed() const; + +protected: + int count_; + bool timeout_; + + // Fills in the actual request to be sent. Note that the transaction ID will + // already be set and cannot be changed. + virtual void Prepare(StunMessage* request) {} + + // Called when the message receives a response or times out. + virtual void OnResponse(StunMessage* response) {} + virtual void OnErrorResponse(StunMessage* response) {} + virtual void OnTimeout() {} + virtual int GetNextDelay(); + +private: + StunRequestManager* manager_; + std::string id_; + StunMessage* msg_; + uint32 tstamp_; + + void set_manager(StunRequestManager* manager); + + friend class StunRequestManager; +}; + +} // namespace cricket + +#endif // __STUNREQUESTMANAGER_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc new file mode 100644 index 0000000..5fc61ac --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.cc @@ -0,0 +1,160 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/bytebuffer.h" +#include "talk/p2p/base/stunserver.h" +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +StunServer::StunServer(talk_base::AsyncUDPSocket* socket) : socket_(socket) { + socket_->SignalReadPacket.connect(this, &StunServer::OnPacket); +} + +StunServer::~StunServer() { + socket_->SignalReadPacket.disconnect(this); +} + +void StunServer::OnPacket( + const char* buf, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + + // TODO: If appropriate, look for the magic cookie before parsing. + + // Parse the STUN message. + talk_base::ByteBuffer bbuf(buf, size); + StunMessage msg; + if (!msg.Read(&bbuf)) { + SendErrorResponse(msg, remote_addr, 400, "Bad Request"); + return; + } + + // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages. + + // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a + // 420 "Unknown Attribute" response. + + // TODO: Check that a message-integrity attribute was given (or send 401 + // "Unauthorized"). Check that a username attribute was given (or send + // 432 "Missing Username"). Look up the username and password. If it + // is missing or the HMAC is wrong, send 431 "Integrity Check Failure". + + // Send the message to the appropriate handler function. + switch (msg.type()) { + case STUN_BINDING_REQUEST: + OnBindingRequest(&msg, remote_addr); + return; + + case STUN_ALLOCATE_REQUEST: + OnAllocateRequest(&msg, remote_addr); + return; + + default: + SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported"); + } +} + +void StunServer::OnBindingRequest( + StunMessage* msg, const talk_base::SocketAddress& remote_addr) { + StunMessage response; + response.SetType(STUN_BINDING_RESPONSE); + response.SetTransactionID(msg->transaction_id()); + + // Tell the user the address that we received their request from. + StunAddressAttribute* mapped_addr = + StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); + mapped_addr->SetFamily(1); + mapped_addr->SetPort(remote_addr.port()); + mapped_addr->SetIP(remote_addr.ip()); + response.AddAttribute(mapped_addr); + + // Tell the user the address that we are sending the response from. + talk_base::SocketAddress local_addr = socket_->GetLocalAddress(); + StunAddressAttribute* source_addr = + StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS); + source_addr->SetFamily(1); + source_addr->SetPort(local_addr.port()); + source_addr->SetIP(local_addr.ip()); + response.AddAttribute(source_addr); + + // TODO: Add username and message-integrity. + + // TODO: Add changed-address. (Keep information about three other servers.) + + SendResponse(response, remote_addr); +} + +void StunServer::OnAllocateRequest( + StunMessage* msg, const talk_base::SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSharedSecretRequest( + StunMessage* msg, const talk_base::SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::OnSendRequest(StunMessage* msg, const talk_base::SocketAddress& addr) { + SendErrorResponse(*msg, addr, 600, "Operation Not Supported"); +} + +void StunServer::SendErrorResponse( + const StunMessage& msg, const talk_base::SocketAddress& addr, int error_code, + const char* error_desc) { + + StunMessage err_msg; + err_msg.SetType(GetStunErrorResponseType(msg.type())); + err_msg.SetTransactionID(msg.transaction_id()); + + StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode(); + err_code->SetErrorClass(error_code / 100); + err_code->SetNumber(error_code % 100); + err_code->SetReason(error_desc); + err_msg.AddAttribute(err_code); + + SendResponse(err_msg, addr); +} + +void StunServer::SendResponse( + const StunMessage& msg, const talk_base::SocketAddress& addr) { + + talk_base::ByteBuffer buf; + msg.Write(&buf); + + // TODO: Allow response addr attribute if sent from another stun server. + + if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0) + std::cerr << "sendto: " << std::strerror(errno) << std::endl; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h new file mode 100644 index 0000000..0e57a18 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver.h @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __STUNSERVER_H__ +#define __STUNSERVER_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/stun.h" + +namespace cricket { + +const int STUN_SERVER_PORT = 3478; + +class StunServer : public sigslot::has_slots<> { +public: + // Creates a STUN server, which will listen on the given socket. + StunServer(talk_base::AsyncUDPSocket* socket); + + // Removes the STUN server from the socket, but does not delete the socket. + ~StunServer(); + +protected: + + // Slot for AsyncSocket.PacketRead: + void OnPacket( + const char* buf, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + + // Handlers for the different types of STUN/TURN requests: + void OnBindingRequest(StunMessage* msg, const talk_base::SocketAddress& addr); + void OnAllocateRequest(StunMessage* msg, const talk_base::SocketAddress& addr); + void OnSharedSecretRequest(StunMessage* msg, const talk_base::SocketAddress& addr); + void OnSendRequest(StunMessage* msg, const talk_base::SocketAddress& addr); + + // Sends an error response to the given message back to the user. + void SendErrorResponse( + const StunMessage& msg, const talk_base::SocketAddress& addr, int error_code, + const char* error_desc); + + // Sends the given message to the appropriate destination. + void SendResponse(const StunMessage& msg, const talk_base::SocketAddress& addr); + +private: + talk_base::AsyncUDPSocket* socket_; +}; + +} // namespace cricket + +#endif // __STUNSERVER_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc new file mode 100644 index 0000000..8aa200e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_main.cc @@ -0,0 +1,66 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/host.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/stunserver.h" +#include <iostream> + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +using namespace cricket; + +int main(int argc, char* argv[]) { + if (argc != 1) { + std::cerr << "usage: stunserver" << std::endl; + return 1; + } + + talk_base::SocketAddress server_addr(talk_base::LocalHost().networks()[1]->ip(), 7000); + + talk_base::Thread *pthMain = talk_base::Thread::Current(); + + talk_base::AsyncUDPSocket* server_socket = talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); + if (server_socket->Bind(server_addr) < 0) { + std::cerr << "bind: " << std::strerror(errno) << std::endl; + return 1; + } + + StunServer* server = new StunServer(server_socket); + + std::cout << "Listening at " << server_addr.ToString() << std::endl; + + pthMain->Run(); + + delete server; + delete server_socket; + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc new file mode 100644 index 0000000..b56da4e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/stunserver_unittest.cc @@ -0,0 +1,107 @@ +#include "talk/base/testclient.h" +#include "talk/base/thread.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/host.h" +#include "talk/p2p/base/stunserver.h" +#include <cstring> +#include <iostream> +#include <cassert> + +using namespace cricket; + +StunMessage* GetResponse(talk_base::TestClient* client) { + talk_base::TestClient::Packet* packet = client->NextPacket(); + assert(packet); + talk_base::ByteBuffer buf(packet->buf, packet->size); + StunMessage* msg = new StunMessage(); + assert(msg->Read(&buf)); + delete packet; + return msg; +} + +int main(int argc, char* argv[]) { + assert(talk_base::LocalHost().networks().size() >= 2); + talk_base::SocketAddress server_addr(talk_base::LocalHost().networks()[1]->ip(), 7000); + talk_base::SocketAddress client_addr(talk_base::LocalHost().networks()[1]->ip(), 6000); + + talk_base::Thread th; + + talk_base::AsyncUDPSocket* server_socket = 0; + StunServer* server = 0; + if (argc >= 2) { + server_addr.SetIP(argv[1]); + client_addr.SetIP(0); + if (argc == 3) + server_addr.SetPort(atoi(argv[2])); + std::cout << "Using server at " << server_addr.ToString() << std::endl; + } else { + server_socket = talk_base::CreateAsyncUDPSocket(th.socketserver()); + assert(server_socket->Bind(server_addr) >= 0); + server = new StunServer(server_socket); + } + + talk_base::AsyncUDPSocket* client_socket = talk_base::CreateAsyncUDPSocket(th.socketserver()); + assert(client_socket->Bind(client_addr) >= 0); + talk_base::TestClient* client = new talk_base::TestClient(client_socket, &th); + + th.Start(); + + const char* bad = "this is a completely nonsensical message whose only " + "purpose is to make the parser go 'ack'. it doesn't " + "look anything like a normal stun message"; + + client->SendTo(bad, std::strlen(bad), server_addr); + StunMessage* msg = GetResponse(client); + assert(msg->type() == STUN_BINDING_ERROR_RESPONSE); + + const StunErrorCodeAttribute* err = msg->GetErrorCode(); + assert(err); + assert(err->error_class() == 4); + assert(err->number() == 0); + assert(err->reason() == std::string("Bad Request")); + + delete msg; + + std::string transaction_id = "0123456789abcdef"; + + StunMessage req; + req.SetType(STUN_BINDING_REQUEST); + req.SetTransactionID(transaction_id); + + talk_base::ByteBuffer buf; + req.Write(&buf); + + client->SendTo(buf.Data(), buf.Length(), server_addr); + StunMessage* msg2 = GetResponse(client); + assert(msg2->type() == STUN_BINDING_RESPONSE); + assert(msg2->transaction_id() == transaction_id); + + const StunAddressAttribute* mapped_addr = + msg2->GetAddress(STUN_ATTR_MAPPED_ADDRESS); + assert(mapped_addr); + assert(mapped_addr->family() == 1); + assert(mapped_addr->port() == client_addr.port()); + if (mapped_addr->ip() != client_addr.ip()) { + printf("Warning: mapped IP (%s) != local IP (%s)\n", + talk_base::SocketAddress::IPToString(mapped_addr->ip()).c_str(), + client_addr.IPAsString().c_str()); + } + + const StunAddressAttribute* source_addr = + msg2->GetAddress(STUN_ATTR_SOURCE_ADDRESS); + assert(source_addr); + assert(source_addr->family() == 1); + assert(source_addr->port() == server_addr.port()); + assert(source_addr->ip() == server_addr.ip()); + + delete msg2; + + th.Stop(); + + delete server; + delete server_socket; + delete client; + + std::cout << "PASS" << std::endl; + return 0; +} diff --git a/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc new file mode 100644 index 0000000..57e2a4f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.cc @@ -0,0 +1,271 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +#include <cassert> +#include <iostream> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#ifdef WIN32 +#include "talk/base/winfirewall.h" +#endif // WIN32 +#include "talk/p2p/base/tcpport.h" + +namespace cricket { + +#ifdef WIN32 +static talk_base::WinFirewall win_firewall; +#endif // WIN32 + +TCPPort::TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, + const talk_base::SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), address_(address), + incoming_only_(address_.port() != 0), error_(0) { + socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent); + if (socket_->Bind(address_) < 0) { + LOG_F(LS_ERROR) << "Bind error: " << socket_->GetError(); + } +} + +TCPPort::~TCPPort() { + delete socket_; +} + +Connection* TCPPort::CreateConnection(const Candidate& address, + CandidateOrigin origin) { + // We only support TCP protocols + if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp")) + return 0; + + // We can't accept TCP connections incoming on other ports + if (origin == ORIGIN_OTHER_PORT) + return 0; + + // Check if we are allowed to make outgoing TCP connections + if (incoming_only_ && (origin == ORIGIN_MESSAGE)) + return 0; + + // We don't know how to act as an ssl server yet + if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT)) + return 0; + + TCPConnection* conn = 0; + if (talk_base::AsyncTCPSocket * socket + = GetIncoming(address.address(), true)) { + socket->SignalReadPacket.disconnect(this); + conn = new TCPConnection(this, address, socket); + } else { + conn = new TCPConnection(this, address); + } + AddConnection(conn); + return conn; +} + +void TCPPort::PrepareAddress() { + assert(socket_); + + bool allow_listen = true; +#ifdef WIN32 + if (win_firewall.Initialize()) { + char module_path[MAX_PATH + 1] = { 0 }; + ::GetModuleFileNameA(NULL, module_path, MAX_PATH); + if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) { + allow_listen = false; + } + } +#endif // WIN32 + if (!allow_listen) { + LOG_F(LS_VERBOSE) << "Not listening due to firewall restrictions"; + } else if (socket_->Listen(5) < 0) { + LOG_F(LS_ERROR) << "Listen error: " << socket_->GetError(); + } + // Note: We still add the address, since otherwise the remote side won't + // recognize our incoming TCP connections. + AddAddress(socket_->GetLocalAddress(), "tcp", true); +} + +int TCPPort::SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload) { + talk_base::AsyncTCPSocket * socket = 0; + + if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) { + socket = conn->socket(); + } else { + socket = GetIncoming(addr); + } + if (!socket) { + LOG_F(LS_ERROR) << "Unknown destination: " << addr.ToString(); + return -1; // TODO: Set error_ + } + + //LOG_F(INFO) << "(" << size << ", " << addr.ToString() << ")"; + + int sent = socket->Send(data, size); + if (sent < 0) { + error_ = socket->GetError(); + LOG_F(LS_ERROR) << "(" << size << ", " << addr.ToString() + << ") Send error: " << error_; + } + return sent; +} + +int TCPPort::SetOption(talk_base::Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int TCPPort::GetError() { + assert(socket_); + return error_; +} + +void TCPPort::OnAcceptEvent(talk_base::AsyncSocket* socket) { + assert(socket == socket_); + + Incoming incoming; + talk_base::AsyncSocket * newsocket + = static_cast<talk_base::AsyncSocket *>(socket->Accept(&incoming.addr)); + if (!newsocket) { + // TODO: Do something better like forwarding the error to the user. + LOG_F(LS_ERROR) << "Accept error: " << socket_->GetError(); + return; + } + incoming.socket = new talk_base::AsyncTCPSocket(newsocket); + incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket); + + LOG_F(LS_VERBOSE) << "(" << incoming.addr.ToString() << ")"; + incoming_.push_back(incoming); + + // Prime a read event in case data is waiting + newsocket->SignalReadEvent(newsocket); +} + +talk_base::AsyncTCPSocket * TCPPort::GetIncoming( + const talk_base::SocketAddress& addr, bool remove) { + talk_base::AsyncTCPSocket * socket = 0; + for (std::list<Incoming>::iterator it = incoming_.begin(); + it != incoming_.end(); ++it) { + if (it->addr == addr) { + socket = it->socket; + if (remove) + incoming_.erase(it); + break; + } + } + return socket; +} + +void TCPPort::OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + Port::OnReadPacket(data, size, remote_addr); +} + +TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, + talk_base::AsyncTCPSocket* socket) + : Connection(port, 0, candidate), socket_(socket), error_(0) { + bool outgoing = (socket_ == 0); + if (outgoing) { + socket_ = static_cast<talk_base::AsyncTCPSocket *>(port->CreatePacketSocket( + (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP)); + } else { + // Incoming connections should match the network address + ASSERT(socket_->GetLocalAddress().EqualIPs(port->address_)); + } + socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); + socket_->SignalClose.connect(this, &TCPConnection::OnClose); + if (outgoing) { + set_connected(false); + talk_base::SocketAddress local_address(port->address_.ip(), 0); + socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); + socket_->Bind(local_address); + socket_->Connect(candidate.address()); + LOG_F(LS_VERBOSE) << "Connecting from " << local_address.ToString() + << " to " << candidate.address().ToString(); + } +} + +TCPConnection::~TCPConnection() { + delete socket_; +} + +int TCPConnection::Send(const void* data, size_t size) { + if (write_state() != STATE_WRITABLE) { + // TODO: Should STATE_WRITE_TIMEOUT return a non-blocking error? + error_ = EWOULDBLOCK; + return SOCKET_ERROR; + } + int sent = socket_->Send(data, size); + if (sent < 0) { + error_ = socket_->GetError(); + } else { + sent_total_bytes_ += sent; + } + return sent; +} + +int TCPConnection::GetError() { + return error_; +} + +TCPPort* TCPConnection::tcpport() { + return static_cast<TCPPort*>(port_); +} + +void TCPConnection::OnConnect(talk_base::AsyncTCPSocket* socket) { + assert(socket == socket_); + LOG_F(LS_VERBOSE) << "(" << socket->GetRemoteAddress().ToString() << ")"; + set_connected(true); +} + +void TCPConnection::OnClose(talk_base::AsyncTCPSocket* socket, int error) { + assert(socket == socket_); + LOG_F(LS_VERBOSE) << "(" << error << ")"; + set_connected(false); + set_write_state(STATE_WRITE_TIMEOUT); +} + +void TCPConnection::OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + assert(socket == socket_); + //LOG_F(LS_INFO) << "(" << size << ", " << remote_addr.ToString() << ")"; + Connection::OnReadPacket(data, size); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h new file mode 100644 index 0000000..24555ae --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/tcpport.h @@ -0,0 +1,122 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __TCPPORT_H__ +#define __TCPPORT_H__ + +#include <list> +#include "talk/base/asynctcpsocket.h" +#include "talk/p2p/base/port.h" + +namespace cricket { + +class TCPConnection; + +extern const std::string LOCAL_PORT_TYPE; // type of TCP ports + +// Communicates using a local TCP port. +// +// This class is designed to allow subclasses to take advantage of the +// connection management provided by this class. A subclass should take of all +// packet sending and preparation, but when a packet is received, it should +// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection. +class TCPPort : public Port { +public: + TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, const talk_base::SocketAddress& address); + virtual ~TCPPort(); + + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual void PrepareAddress(); + + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + +protected: + // Handles sending using the local TCP socket. + virtual int SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload); + + // Creates TCPConnection for incoming sockets + void OnAcceptEvent(talk_base::AsyncSocket* socket); + + talk_base::AsyncSocket* socket() { return socket_; } + +private: + // Note: use this until Network ips are stable, then use network->ip + talk_base::SocketAddress address_; + bool incoming_only_; + talk_base::AsyncSocket* socket_; + int error_; + + struct Incoming { + talk_base::SocketAddress addr; + talk_base::AsyncTCPSocket * socket; + }; + std::list<Incoming> incoming_; + + talk_base::AsyncTCPSocket * GetIncoming(const talk_base::SocketAddress& addr, + bool remove = false); + + // Receives packet signal from the local TCP Socket. + void OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + + friend class TCPConnection; +}; + +class TCPConnection : public Connection { +public: + // Connection is outgoing unless socket is specified + TCPConnection(TCPPort* port, const Candidate& candidate, + talk_base::AsyncTCPSocket* socket = 0); + virtual ~TCPConnection(); + + virtual int Send(const void* data, size_t size); + virtual int GetError(); + + talk_base::AsyncTCPSocket * socket() { return socket_; } + +private: + TCPPort* tcpport(); + talk_base::AsyncTCPSocket* socket_; + int error_; + + void OnConnect(talk_base::AsyncTCPSocket* socket); + void OnClose(talk_base::AsyncTCPSocket* socket, int error); + void OnReadPacket(const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); + + friend class TCPPort; +}; + +} // namespace cricket + +#endif // __TCPPORT_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transport.cc b/Plugins/jingle/libjingle/talk/p2p/base/transport.cc new file mode 100644 index 0000000..dfa2dfd --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transport.cc @@ -0,0 +1,441 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/common.h" +#include "talk/p2p/base/transport.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/transportchannelimpl.h" +#include "talk/p2p/base/constants.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace { + +struct ChannelParams { + std::string name; + std::string session_type; + cricket::TransportChannelImpl* channel; + buzz::XmlElement* elem; + + ChannelParams() : channel(NULL), elem(NULL) {} +}; +typedef talk_base::TypedMessageData<ChannelParams*> ChannelMessage; + +const int MSG_CREATECHANNEL = 1; +const int MSG_DESTROYCHANNEL = 2; +const int MSG_DESTROYALLCHANNELS = 3; +const int MSG_CONNECTCHANNELS = 4; +const int MSG_RESETCHANNELS = 5; +const int MSG_ONSIGNALINGREADY = 6; +const int MSG_FORWARDCHANNELMESSAGE = 7; +const int MSG_READSTATE = 8; +const int MSG_WRITESTATE = 9; +const int MSG_REQUESTSIGNALING = 10; +const int MSG_ONCHANNELMESSAGE = 11; +const int MSG_CONNECTING = 12; + +} // namespace + +namespace cricket { + +Transport::Transport(SessionManager* session_manager, const std::string& name) + : session_manager_(session_manager), name_(name), destroyed_(false), + readable_(false), writable_(false), connect_requested_(false), + allow_local_ips_(false) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); +} + +Transport::~Transport() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(destroyed_); +} + +TransportChannelImpl* Transport::CreateChannel(const std::string& name, const std::string &session_type) { + ChannelParams params; + params.name = name; + params.session_type = session_type; + ChannelMessage msg(¶ms); + session_manager_->worker_thread()->Send(this, MSG_CREATECHANNEL, &msg); + return msg.data()->channel; +} + +TransportChannelImpl* Transport::CreateChannel_w(const std::string& name, const std::string &session_type) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + + TransportChannelImpl* impl = CreateTransportChannel(name, session_type); + impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState); + impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState); + impl->SignalRequestSignaling.connect( + this, &Transport::OnChannelRequestSignaling); + impl->SignalChannelMessage.connect(this, &Transport::OnChannelMessage); + + talk_base::CritScope cs(&crit_); + ASSERT(channels_.find(name) == channels_.end()); + channels_[name] = impl; + destroyed_ = false; + if (connect_requested_) { + impl->Connect(); + if (channels_.size() == 1) { + // If this is the first channel, then indicate that we have started + // connecting. + session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL); + } + } + return impl; +} + +TransportChannelImpl* Transport::GetChannel(const std::string& name) { + talk_base::CritScope cs(&crit_); + ChannelMap::iterator iter = channels_.find(name); + return (iter != channels_.end()) ? iter->second : NULL; +} + +bool Transport::HasChannels() { + talk_base::CritScope cs(&crit_); + return !channels_.empty(); +} + +void Transport::DestroyChannel(const std::string& name) { + ChannelParams params; + params.name = name; + ChannelMessage msg(¶ms); + session_manager_->worker_thread()->Send(this, MSG_DESTROYCHANNEL, &msg); +} + +void Transport::DestroyChannel_w(const std::string& name) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + TransportChannelImpl* impl = NULL; + { + talk_base::CritScope cs(&crit_); + ChannelMap::iterator iter = channels_.find(name); + ASSERT(iter != channels_.end()); + impl = iter->second; + channels_.erase(iter); + } + + if (connect_requested_ && channels_.empty()) { + // We're not longer attempting to connect. + session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL); + } + + if (impl) + DestroyTransportChannel(impl); +} + +void Transport::ConnectChannels() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + session_manager_->worker_thread()->Post(this, MSG_CONNECTCHANNELS, NULL); +} + +void Transport::ConnectChannels_w() { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + if (connect_requested_) + return; + connect_requested_ = true; + session_manager_->signaling_thread()->Post(this, MSG_ONCHANNELMESSAGE, NULL); + CallChannels_w(&TransportChannelImpl::Connect); + if (!channels_.empty()) { + session_manager_->signaling_thread()->Post(this, MSG_CONNECTING, NULL); + } +} + +void Transport::OnConnecting_s() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SignalConnecting(this); +} + +void Transport::DestroyAllChannels() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + session_manager_->worker_thread()->Send(this, MSG_DESTROYALLCHANNELS, NULL); + destroyed_ = true; +} + +void Transport::DestroyAllChannels_w() { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + std::vector<TransportChannelImpl*> impls; + { + talk_base::CritScope cs(&crit_); + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + impls.push_back(iter->second); + } + channels_.clear(); + } + + for (size_t i = 0; i < impls.size(); ++i) + DestroyTransportChannel(impls[i]); +} + +void Transport::ResetChannels() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + session_manager_->worker_thread()->Post(this, MSG_RESETCHANNELS, NULL); +} + +void Transport::ResetChannels_w() { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + + // We are no longer attempting to connect + connect_requested_ = false; + + // Clear out the old messages, they aren't relevant + talk_base::CritScope cs(&crit_); + for (size_t i=0; i<messages_.size(); ++i) { + delete messages_[i]; + } + messages_.clear(); + + // Reset all of the channels + CallChannels_w(&TransportChannelImpl::Reset); +} + +void Transport::OnSignalingReady() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL); + + // Notify the subclass. + OnTransportSignalingReady(); +} + +void Transport::CallChannels_w(TransportChannelFunc func) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + talk_base::CritScope cs(&crit_); + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + ((iter->second)->*func)(); + } +} + +void Transport::ForwardChannelMessage(const std::string& name, + buzz::XmlElement* elem) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(HasChannel(name)); + ChannelParams* params = new ChannelParams(); + params->name = name; + params->elem = elem; + ChannelMessage* msg = new ChannelMessage(params); + session_manager_->worker_thread()->Post(this, MSG_FORWARDCHANNELMESSAGE, msg); +} + +void Transport::ForwardChannelMessage_w(const std::string& name, + buzz::XmlElement* elem) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + ChannelMap::iterator iter = channels_.find(name); + // It's ok for a channel to go away while this message is in transit. + if (iter != channels_.end()) { + iter->second->OnChannelMessage(elem); + } + delete elem; +} + +void Transport::OnChannelReadableState(TransportChannel* channel) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + session_manager_->signaling_thread()->Post(this, MSG_READSTATE, NULL); +} + +void Transport::OnChannelReadableState_s() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + bool readable = GetTransportState_s(true); + if (readable_ != readable) { + readable_ = readable; + SignalReadableState(this); + } +} + +void Transport::OnChannelWritableState(TransportChannel* channel) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + session_manager_->signaling_thread()->Post(this, MSG_WRITESTATE, NULL); +} + +void Transport::OnChannelWritableState_s() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + bool writable = GetTransportState_s(false); + if (writable_ != writable) { + writable_ = writable; + SignalWritableState(this); + } +} + +bool Transport::GetTransportState_s(bool read) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + bool result = false; + talk_base::CritScope cs(&crit_); + for (ChannelMap::iterator iter = channels_.begin(); + iter != channels_.end(); + ++iter) { + bool b = (read ? iter->second->readable() : iter->second->writable()); + result = result || b; + } + return result; +} + +void Transport::OnChannelRequestSignaling() { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + session_manager_->signaling_thread()->Post(this, MSG_REQUESTSIGNALING, NULL); +} + +void Transport::OnChannelRequestSignaling_s() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + SignalRequestSignaling(this); +} + +void Transport::OnChannelMessage(TransportChannelImpl* impl, + buzz::XmlElement* elem) { + ASSERT(session_manager_->worker_thread()->IsCurrent()); + talk_base::CritScope cs(&crit_); + messages_.push_back(elem); + + // We hold any messages until the client lets us connect. + if (connect_requested_) { + session_manager_->signaling_thread()->Post( + this, MSG_ONCHANNELMESSAGE, NULL); + } +} + +void Transport::OnChannelMessage_s() { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + ASSERT(connect_requested_); + + std::vector<buzz::XmlElement*> msgs; + { + talk_base::CritScope cs(&crit_); + msgs.swap(messages_); + } + + if (!msgs.empty()) + OnTransportChannelMessages(msgs); +} + +void Transport::OnTransportChannelMessages( + const std::vector<buzz::XmlElement*>& msgs) { + std::vector<buzz::XmlElement*> elems; + for (size_t i = 0; i < msgs.size(); ++i) { + buzz::XmlElement* elem = + new buzz::XmlElement(buzz::QName(name(), "transport")); + elem->AddElement(msgs[i]); + elems.push_back(elem); + } + SignalTransportMessage(this, elems); +} + +void Transport::OnMessage(talk_base::Message* msg) { + switch (msg->message_id) { + case MSG_CREATECHANNEL: + { + ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data(); + params->channel = CreateChannel_w(params->name, params->session_type); + } + break; + case MSG_DESTROYCHANNEL: + { + ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data(); + DestroyChannel_w(params->name); + } + break; + case MSG_CONNECTCHANNELS: + ConnectChannels_w(); + break; + case MSG_RESETCHANNELS: + ResetChannels_w(); + break; + case MSG_DESTROYALLCHANNELS: + DestroyAllChannels_w(); + break; + case MSG_ONSIGNALINGREADY: + CallChannels_w(&TransportChannelImpl::OnSignalingReady); + break; + case MSG_FORWARDCHANNELMESSAGE: + { + ChannelParams* params = static_cast<ChannelMessage*>(msg->pdata)->data(); + ForwardChannelMessage_w(params->name, params->elem); + delete params; + } + break; + case MSG_CONNECTING: + OnConnecting_s(); + break; + case MSG_READSTATE: + OnChannelReadableState_s(); + break; + case MSG_WRITESTATE: + OnChannelWritableState_s(); + break; + case MSG_REQUESTSIGNALING: + OnChannelRequestSignaling_s(); + break; + case MSG_ONCHANNELMESSAGE: + OnChannelMessage_s(); + break; + } +} + +bool Transport::BadRequest(const buzz::XmlElement* stanza, + const std::string& text, + const buzz::XmlElement* extra_info) { + SignalTransportError(this, stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + text, extra_info); + return false; +} + +bool Transport::ParseAddress(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + talk_base::SocketAddress* address) { + ASSERT(elem->HasAttr(QN_ADDRESS)); + ASSERT(elem->HasAttr(QN_PORT)); + + // Record the parts of the address. + address->SetIP(elem->Attr(QN_ADDRESS)); + std::istringstream ist(elem->Attr(QN_PORT)); + int port; + ist >> port; + address->SetPort(port); + + // No address zero. + if (address->IsAny()) + return BadRequest(stanza, "candidate has address of zero", NULL); + + // Always disallow addresses that refer to the local host. + if (address->IsLocalIP() && !allow_local_ips_) + return BadRequest(stanza, "candidate has local IP address", NULL); + + // Disallow all ports below 1024, except for 80 and 443 on public addresses. + if (port < 1024) { + if ((port != 80) && (port != 443)) + return BadRequest(stanza, + "candidate has port below 1024, but not 80 or 443", + NULL); + if (address->IsPrivateIP()) { + return BadRequest(stanza, "candidate has port of 80 or 443 with private " + "IP address", NULL); + } + } + + return true; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transport.h b/Plugins/jingle/libjingle/talk/p2p/base/transport.h new file mode 100644 index 0000000..826fd26 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transport.h @@ -0,0 +1,277 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// A Transport manages a set of named channels of the same type. +// +// Subclasses choose the appropriate class to instantiate for each channel; +// however, this base class keeps track of the channels by name, watches their +// state changes (in order to update the manager's state), and forwards +// requests to begin connecting or to reset to each of the channels. +// +// On Threading: Transport performs work on both the signaling and worker +// threads. For subclasses, the rule is that all signaling related calls will +// be made on the signaling thread and all channel related calls (including +// signaling for a channel) will be made on the worker thread. When +// information needs to be sent between the two threads, this class should do +// the work (e.g., ForwardChannelMessage). +// +// Note: Subclasses must call DestroyChannels() in their own constructors. +// It is not possible to do so here because the subclass constructor will +// already have run. + +#ifndef _CRICKET_P2P_BASE_TRANSPORT_H_ +#define _CRICKET_P2P_BASE_TRANSPORT_H_ + +#include <string> +#include <map> +#include <vector> +#include "talk/base/criticalsection.h" +#include "talk/base/messagequeue.h" +#include "talk/base/sigslot.h" + +namespace buzz { +class QName; +class XmlElement; +} + +namespace cricket { + +class SessionManager; +class Session; +class TransportChannel; +class TransportChannelImpl; + +class Transport : public talk_base::MessageHandler, public sigslot::has_slots<> { + public: + Transport(SessionManager* session_manager, const std::string& name); + virtual ~Transport(); + + // Returns a pointer to the singleton session manager. + SessionManager* session_manager() const { return session_manager_; } + + // Returns the name of this transport. + const std::string& name() const { return name_; } + + // Returns the readable and states of this manager. These bits are the ORs + // of the corresponding bits on the managed channels. Each time one of these + // states changes, a signal is raised. + bool readable() const { return readable_; } + bool writable() const { return writable_; } + sigslot::signal1<Transport*> SignalReadableState; + sigslot::signal1<Transport*> SignalWritableState; + + // Returns whether the client has requested the channels to connect. + bool connect_requested() const { return connect_requested_; } + + // Create, destroy, and lookup the channels of this type by their names. + TransportChannelImpl* CreateChannel(const std::string& name, const std::string &session_type); + // Note: GetChannel may lead to race conditions, since the mutex is not held + // after the pointer is returned. + TransportChannelImpl* GetChannel(const std::string& name); + // Note: HasChannel does not lead to race conditions, unlike GetChannel. + bool HasChannel(const std::string& name) { return (NULL != GetChannel(name)); } + bool HasChannels(); + void DestroyChannel(const std::string& name); + + // Tells all current and future channels to start connecting. When the first + // channel begins connecting, the following signal is raised. + void ConnectChannels(); + sigslot::signal1<Transport*> SignalConnecting; + + // Resets all of the channels back to their initial state. They are no + // longer connecting. + void ResetChannels(); + + // Destroys every channel created so far. + void DestroyAllChannels(); + + // The session handshake includes negotiation of both the application and the + // transport. The initiating transport creates an "offer" describing what + // options it supports and the responding transport creates an "answer" + // describing which options it has accepted. If OnTransport* returns false, + // that indicates that no acceptable options given and this transport cannot + // be negotiated. + // + // The transport negotiation operates as follows. When the initiating client + // creates the session, but before they send the initiate, we create the + // supported transports. The client may override these, but otherwise they + // get a default set. When the initiate is sent, we ask each transport to + // produce an offer. When the receiving client gets the initiate, they will + // iterate through the transport offers in order of their own preference. + // For each one, they create the transport (if they know what it is) and + // call OnTransportOffer. If this returns true, then we're good; otherwise, + // we continue iterating. If no transport works, then we reject the session. + // Otherwise, we have a single transport, and we send back a transport-accept + // message that contains the answer. When this arrives at the initiating + // client, we destroy all transports but the one in the answer and then pass + // the answer to it. If this transport cannot be found or it cannot accept + // the answer, then we reject the session. Otherwise, we're in good shape. + virtual buzz::XmlElement* CreateTransportOffer() = 0; + virtual buzz::XmlElement* CreateTransportAnswer() = 0; + virtual bool OnTransportOffer(const buzz::XmlElement* elem) = 0; + virtual bool OnTransportAnswer(const buzz::XmlElement* elem) = 0; + + // Before any stanza is sent, the manager will request signaling. Once + // signaling is available, the client should call OnSignalingReady. Once + // this occurs, the transport (or its channels) can send any waiting stanzas. + // OnSignalingReady invokes OnTransportSignalingReady and then forwards this + // signal to each channel. + sigslot::signal1<Transport*> SignalRequestSignaling; + void OnSignalingReady(); + + // Handles sending and receiving of stanzas related to negotiating the + // connections of the channels. Different transports may have very different + // signaling, so the XML is handled by the subclass. The msg variable holds + // the element whose name matches this transport, while stanza holds the + // entire stanza. The latter is needed when sending an error response. + // SignalTransportMessage is given the elements that will become the children + // of the transport-info message. Each element must have a name that matches + // the transport's name. + virtual bool OnTransportMessage(const buzz::XmlElement* msg, + const buzz::XmlElement* stanza) = 0; + sigslot::signal2<Transport*, const std::vector<buzz::XmlElement*>&> + SignalTransportMessage; + + // A transport message has generated an transport-specific error. The + // stanza that caused the error is available in session_msg. If false is + // returned, the error is considered unrecoverable, and the session is + // terminated. + virtual bool OnTransportError(const buzz::XmlElement* session_msg, + const buzz::XmlElement* error) = 0; + sigslot::signal6<Transport*, const buzz::XmlElement*, const buzz::QName&, + const std::string&, const std::string&, + const buzz::XmlElement*> + SignalTransportError; + + sigslot::signal2<Transport*, const std::string&> SignalChannelGone; + + // (For testing purposes only.) This indicates whether we will allow local + // IPs (e.g. 127.*) to be used as addresses for P2P. + bool allow_local_ips() const { return allow_local_ips_; } + void set_allow_local_ips(bool value) { allow_local_ips_ = value; } + + protected: + // Helper function to bad-request error for a stanza passed to + // OnTransportMessage. Returns false. + bool BadRequest(const buzz::XmlElement* stanza, const std::string& text, + const buzz::XmlElement* extra_info); + + // Helper function to parse an element describing an address. This retrieves + // the IP and port from the given element (using QN_ADDRESS and QN_PORT) and + // verifies that they look like plausible values. + bool ParseAddress(const buzz::XmlElement* stanza, + const buzz::XmlElement* elem, + talk_base::SocketAddress* address); + + // These are called by Create/DestroyChannel above in order to create or + // destroy the appropriate type of channel. + virtual TransportChannelImpl* CreateTransportChannel( + const std::string& name, const std::string &session_type) = 0; + virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0; + + // Informs the subclass that we received the signaling ready message. + virtual void OnTransportSignalingReady() {} + + // Forwards the given XML element to the channel on the worker thread. This + // occurs asynchronously, so we take ownership of the element. Furthermore, + // the channel will not be able to return an error if the XML is invalid, so + // the transport should have checked its validity already. + void ForwardChannelMessage(const std::string& name, + buzz::XmlElement* elem); + + // Handles a set of messages sent by the channels. The default + // implementation simply forwards each as its own transport message by + // wrapping it in an element identifying this transport and then invoking + // SignalTransportMessage. Smarter transports may be able to place multiple + // channel messages within one transport message. + // + // Note: The implementor of this method is responsible for deleting the XML + // elements passed in, unless they are sent to SignalTransportMessage, where + // the receiver will delete them. + virtual void OnTransportChannelMessages( + const std::vector<buzz::XmlElement*>& msgs); + + private: + typedef std::map<std::string, TransportChannelImpl*> ChannelMap; + typedef std::vector<buzz::XmlElement*> XmlElementList; + + SessionManager* session_manager_; + std::string name_; + bool destroyed_; + bool readable_; + bool writable_; + bool connect_requested_; + ChannelMap channels_; + XmlElementList messages_; + talk_base::CriticalSection crit_; // Protects changes to channels and messages + bool allow_local_ips_; + + // Called when the state of a channel changes. + void OnChannelReadableState(TransportChannel* channel); + void OnChannelWritableState(TransportChannel* channel); + + // Called when a channel requests signaling. + void OnChannelRequestSignaling(); + + // Called when a channel wishes to send a transport message. + void OnChannelMessage(TransportChannelImpl* impl, buzz::XmlElement* elem); + + // Dispatches messages to the appropriate handler (below). + void OnMessage(talk_base::Message* msg); + + // These are versions of the above methods that are called only on a + // particular thread (s = signaling, w = worker). The above methods post or + // send a message to invoke this version. + TransportChannelImpl* CreateChannel_w(const std::string& name, const std::string& session_type); + void DestroyChannel_w(const std::string& name); + void ConnectChannels_w(); + void ResetChannels_w(); + void DestroyAllChannels_w(); + void ForwardChannelMessage_w(const std::string& name, + buzz::XmlElement* elem); + void OnChannelReadableState_s(); + void OnChannelWritableState_s(); + void OnChannelRequestSignaling_s(); + void OnConnecting_s(); + + // Helper function that invokes the given function on every channel. + typedef void (TransportChannelImpl::* TransportChannelFunc)(); + void CallChannels_w(TransportChannelFunc func); + + // Computes the OR of the channel's read or write state (argument picks). + bool GetTransportState_s(bool read); + + // Invoked when there are messages waiting to send in the messages_ list. + // We wait to send any messages until the client asks us to connect. + void OnChannelMessage_s(); + + DISALLOW_EVIL_CONSTRUCTORS(Transport); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_TRANSPORT_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc new file mode 100644 index 0000000..ba076ac --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.cc @@ -0,0 +1,56 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sstream> +#include "talk/p2p/base/transportchannel.h" + +namespace cricket { + +std::string TransportChannel::ToString() const { + const char READABLE_ABBREV[2] = { '_', 'R' }; + const char WRITABLE_ABBREV[2] = { '_', 'W' }; + std::stringstream ss; + ss << "Channel[" << name_ << "|" << READABLE_ABBREV[readable_] + << WRITABLE_ABBREV[writable_] << "]"; + return ss.str(); +} + +void TransportChannel::set_readable(bool readable) { + if (readable_ != readable) { + readable_ = readable; + SignalReadableState(this); + } +} + +void TransportChannel::set_writable(bool writable) { + if (writable_ != writable) { + writable_ = writable; + SignalWritableState(this); + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h new file mode 100644 index 0000000..68b8fd1 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannel.h @@ -0,0 +1,104 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_ +#define _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_ + +#include <string> +#include "talk/base/basictypes.h" +#include "talk/base/sigslot.h" +#include "talk/base/socket.h" + +namespace cricket { + +// A TransportChannel represents one logical stream of packets that are sent +// between the two sides of a session. +class TransportChannel: public sigslot::has_slots<> { + public: + TransportChannel(const std::string& name, const std::string &session_type) + : name_(name), session_type_(session_type), readable_(false), writable_(false) {} + virtual ~TransportChannel() {} + + // Returns the name of this channel. + const std::string& name() const { return name_; } + const std::string& session_type() const { return session_type_; } + + // Returns the readable and states of this channel. Each time one of these + // states changes, a signal is raised. These states are aggregated by the + // TransportManager. + bool readable() const { return readable_; } + bool writable() const { return writable_; } + sigslot::signal1<TransportChannel*> SignalReadableState; + sigslot::signal1<TransportChannel*> SignalWritableState; + + // Attempts to send the given packet. The return value is < 0 on failure. + virtual int SendPacket(const char *data, size_t len) = 0; + + // Sets a socket option on this channel. Note that not all options are + // supported by all transport types. + virtual int SetOption(talk_base::Socket::Option opt, int value) = 0; + + // Returns the most recent error that occurred on this channel. + virtual int GetError() = 0; + + // Signalled each time a packet is received on this channel. + sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket; + + // This signal occurs when there is a change in the way that packets are + // being routed. The address indicates the address of the first hop in the + // new route, if this is known. If this cannot be determined or is not well- + // defined, then the channel may give an address of 0. + sigslot::signal2<TransportChannel*, const talk_base::SocketAddress&> + SignalRouteChange; + + // Invoked when the channel is being destroyed. + sigslot::signal1<TransportChannel*> SignalDestroyed; + + // TODO: Generalize network monitoring. + + // Debugging description of this transport channel. + std::string ToString() const; + + protected: + // Sets the readable state, signaling if necessary. + void set_readable(bool readable); + + // Sets the writable state, signaling if necessary. + void set_writable(bool writable); + + private: + std::string name_; + std::string session_type_; + bool readable_; + bool writable_; + + DISALLOW_EVIL_CONSTRUCTORS(TransportChannel); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNEL_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h new file mode 100644 index 0000000..742d307 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelimpl.h @@ -0,0 +1,81 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_ +#define _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_ + +#include <string> +#include "talk/p2p/base/transportchannel.h" + +namespace buzz { class XmlElement; } + +namespace cricket { + +class Transport; + +// Base class for real implementations of TransportChannel. This includes some +// methods called only by Transport, which do not need to be exposed to the +// client. +class TransportChannelImpl: public TransportChannel { + public: + TransportChannelImpl(const std::string& name, const std::string& session_type) + : TransportChannel(name, session_type) {} + + // Returns the transport that created this channel. + virtual Transport* GetTransport() = 0; + + // Begins the process of attempting to make a connection to the other client. + virtual void Connect() = 0; + + // Resets this channel back to the initial state (i.e., not connecting). + virtual void Reset() = 0; + + // Allows an individual channel to request signaling and be notified when it + // is ready. This is useful if the individual named channels have need to + // send their own transport-info stanzas. + sigslot::signal0<> SignalRequestSignaling; + virtual void OnSignalingReady() = 0; + + // Handles sending and receiving of stanzas related to this particular + // channel. Any channel may send whatever messages it wants. The Transport + // receives all incoming messages and may forward them to the relevant + // channel. The transport will delete signaled messages. + // + // Note: Since these messages are delivered asynchronously to the channel, + // they cannot return an error if the message is invalid. It is assumed that + // the Transport will have checked validity before forwarding. + virtual void OnChannelMessage(const buzz::XmlElement* msg) = 0; + sigslot::signal2<TransportChannelImpl*, + buzz::XmlElement*> SignalChannelMessage; + + private: + DISALLOW_EVIL_CONSTRUCTORS(TransportChannelImpl); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNELIMPL_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc new file mode 100644 index 0000000..bc94d67 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.cc @@ -0,0 +1,99 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/base/transportchannelproxy.h" +#include "talk/base/common.h" +#include "talk/p2p/base/transport.h" +#include "talk/p2p/base/transportchannelimpl.h" + +namespace cricket { + +TransportChannelProxy::TransportChannelProxy(const std::string& name, const std::string &session_type) + : TransportChannel(name, session_type), impl_(NULL) { +} + +TransportChannelProxy::~TransportChannelProxy() { + if (impl_) + impl_->GetTransport()->DestroyChannel(impl_->name()); +} + +void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { + impl_ = impl; + impl_->SignalReadableState.connect( + this, &TransportChannelProxy::OnReadableState); + impl_->SignalWritableState.connect( + this, &TransportChannelProxy::OnWritableState); + impl_->SignalReadPacket.connect(this, &TransportChannelProxy::OnReadPacket); + impl_->SignalRouteChange.connect(this, &TransportChannelProxy::OnRouteChange); + for (OptionList::iterator it = pending_options_.begin(); + it != pending_options_.end(); + ++it) { + impl_->SetOption(it->first, it->second); + } + pending_options_.clear(); +} + +int TransportChannelProxy::SendPacket(const char *data, size_t len) { + ASSERT(impl_ != NULL); // should not be used until channel is writable + return impl_->SendPacket(data, len); +} + +int TransportChannelProxy::SetOption(talk_base::Socket::Option opt, int value) { + if (impl_) + return impl_->SetOption(opt, value); + pending_options_.push_back(OptionPair(opt, value)); + return 0; +} + +int TransportChannelProxy::GetError() { + ASSERT(impl_ != NULL); // should not be used until channel is writable + return impl_->GetError(); +} + +void TransportChannelProxy::OnReadableState(TransportChannel* channel) { + ASSERT(channel == impl_); + set_readable(impl_->readable()); +} + +void TransportChannelProxy::OnWritableState(TransportChannel* channel) { + ASSERT(channel == impl_); + set_writable(impl_->writable()); +} + +void TransportChannelProxy::OnReadPacket( + TransportChannel* channel, const char* data, size_t size) { + ASSERT(channel == impl_); + SignalReadPacket(this, data, size); +} + +void TransportChannelProxy::OnRouteChange(TransportChannel* channel, + const talk_base::SocketAddress& address) { + ASSERT(channel == impl_); + SignalRouteChange(this, address); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h new file mode 100644 index 0000000..46d8a44 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/transportchannelproxy.h @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_ +#define _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_ + +#include <string> +#include <vector> +#include "talk/p2p/base/transportchannel.h" + +namespace cricket { + +class TransportChannelImpl; + +// Proxies calls between the client and the transport channel implementation. +// This is needed because clients are allowed to create channels before the +// network negotiation is complete. Hence, we create a proxy up front, and +// when negotiation completes, connect the proxy to the implementaiton. +class TransportChannelProxy: public TransportChannel { + public: + TransportChannelProxy(const std::string& name, const std::string &session_type); + virtual ~TransportChannelProxy(); + + TransportChannelImpl* impl() const { return impl_; } + + // Sets the implementation to which we will proxy. + void SetImplementation(TransportChannelImpl* impl); + + // Implementation of the TransportChannel interface. These simply forward to + // the implementation. + virtual int SendPacket(const char *data, size_t len); + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + + private: + typedef std::pair<talk_base::Socket::Option, int> OptionPair; + typedef std::vector<OptionPair> OptionList; + TransportChannelImpl* impl_; + OptionList pending_options_; + + // Catch signals from the implementation channel. These just forward to the + // client (after updating our state to match). + void OnReadableState(TransportChannel* channel); + void OnWritableState(TransportChannel* channel); + void OnReadPacket(TransportChannel* channel, const char* data, size_t size); + void OnRouteChange(TransportChannel* channel, const talk_base::SocketAddress& address); + + DISALLOW_EVIL_CONSTRUCTORS(TransportChannelProxy); +}; + +} // namespace cricket + +#endif // _CRICKET_P2P_BASE_TRANSPORTCHANNELPROXY_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc b/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc new file mode 100644 index 0000000..fa47eba --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/udpport.cc @@ -0,0 +1,121 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/logging.h" +#include "talk/p2p/base/udpport.h" +#include <iostream> +#include <cassert> + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std { + using ::strerror; +} +#endif + +#ifdef POSIX +extern "C" { +#include <errno.h> +} +#endif // POSIX + +namespace cricket { + +const std::string LOCAL_PORT_TYPE("local"); + +UDPPort::UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, + const talk_base::SocketAddress& address) + : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) { + socket_ = CreatePacketSocket(PROTO_UDP); + socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot); + if (socket_->Bind(address) < 0) + PLOG(LERROR, socket_->GetError()) << "bind"; +} + +UDPPort::UDPPort(talk_base::Thread* thread, const std::string &type, + talk_base::SocketFactory* factory, talk_base::Network* network) + : Port(thread, type, factory, network), socket_(0), error_(0) { +} + +UDPPort::~UDPPort() { + delete socket_; +} + +void UDPPort::PrepareAddress() { + assert(socket_); + AddAddress(socket_->GetLocalAddress(), "udp", true); +} + +Connection* UDPPort::CreateConnection(const Candidate& address, + CandidateOrigin origin) { + if (address.protocol() != "udp") + return 0; + + Connection * conn = new ProxyConnection(this, 0, address); + AddConnection(conn); + return conn; +} + +int UDPPort::SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload) { + assert(socket_); + int sent = socket_->SendTo(data, size, addr); + if (sent < 0) + error_ = socket_->GetError(); + return sent; +} + +int UDPPort::SetOption(talk_base::Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int UDPPort::GetError() { + assert(socket_); + return error_; +} + +void UDPPort::OnReadPacketSlot( + const char* data, size_t size, const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket) { + assert(socket == socket_); + OnReadPacket(data, size, remote_addr); +} + +void UDPPort::OnReadPacket( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { + if (Connection* conn = GetConnection(remote_addr)) { + conn->OnReadPacket(data, size); + } else { + Port::OnReadPacket(data, size, remote_addr); + } +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/base/udpport.h b/Plugins/jingle/libjingle/talk/p2p/base/udpport.h new file mode 100644 index 0000000..9fcfa69 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/base/udpport.h @@ -0,0 +1,90 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __UDPPORT_H__ +#define __UDPPORT_H__ + +#include "talk/base/asyncudpsocket.h" +#include "talk/p2p/base/port.h" + +namespace talk_base { + class Thread; + class Network; + class SocketAddress; +} // namespace talk_base + +namespace cricket { + +extern const std::string LOCAL_PORT_TYPE; // type of UDP ports + +// Communicates using a local UDP port. +// +// This class is designed to allow subclasses to take advantage of the +// connection management provided by this class. A subclass should take of all +// packet sending and preparation, but when a packet is received, it should +// call this UDPPort::OnReadPacket (3 arg) to dispatch to a connection. +class UDPPort : public Port { +public: + UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, + talk_base::Network* network, const talk_base::SocketAddress& address); + virtual ~UDPPort(); + + virtual void PrepareAddress(); + virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin); + + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError(); + +protected: + UDPPort(talk_base::Thread* thread, const std::string &type, + talk_base::SocketFactory* factory, talk_base::Network* network); + + // Handles sending using the local UDP socket. + virtual int SendTo(const void* data, size_t size, + const talk_base::SocketAddress& addr, bool payload); + + // Dispatches the given packet to the port or connection as appropriate. + void OnReadPacket( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); + + talk_base::AsyncPacketSocket* socket() { return socket_; } + +private: + talk_base::AsyncPacketSocket* socket_; + int error_; + + // Receives packet signal from the local UDP Socket. + void OnReadPacketSlot( + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket); +}; + +} // namespace cricket + +#endif // __UDPPORT_H__ diff --git a/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc new file mode 100644 index 0000000..b7960f4 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.cc @@ -0,0 +1,690 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif + +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/base/host.h" +#include "talk/base/logging.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/p2p/base/common.h" +#include "talk/p2p/base/port.h" +#include "talk/p2p/base/relayport.h" +#include "talk/p2p/base/stunport.h" +#include "talk/p2p/base/tcpport.h" +#include "talk/p2p/base/udpport.h" + +namespace { + +const uint32 MSG_CONFIG_START = 1; +const uint32 MSG_CONFIG_READY = 2; +const uint32 MSG_ALLOCATE = 3; +const uint32 MSG_ALLOCATION_PHASE = 4; +const uint32 MSG_SHAKE = 5; + +const uint32 ALLOCATE_DELAY = 250; +const uint32 ALLOCATION_STEP_DELAY = 1 * 1000; + +const int PHASE_UDP = 0; +const int PHASE_RELAY = 1; +const int PHASE_TCP = 2; +const int PHASE_SSLTCP = 3; +const int kNumPhases = 4; + +const float PREF_LOCAL_UDP = 1.0f; +const float PREF_LOCAL_STUN = 0.9f; +const float PREF_LOCAL_TCP = 0.8f; +const float PREF_RELAY = 0.5f; + +const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants +const float RELAY_BACKUP_PREF_MODIFIER = -0.2f; + + +// Returns the phase in which a given local candidate (or rather, the port that +// gave rise to that local candidate) would have been created. +int LocalCandidateToPhase(const cricket::Candidate& candidate) { + cricket::ProtocolType proto; + bool result = cricket::StringToProto(candidate.protocol().c_str(), proto); + if (result) { + if (candidate.type() == cricket::LOCAL_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_UDP; + case cricket::PROTO_TCP: return PHASE_TCP; + default: assert(false); + } + } else if (candidate.type() == cricket::STUN_PORT_TYPE) { + return PHASE_UDP; + } else if (candidate.type() == cricket::RELAY_PORT_TYPE) { + switch (proto) { + case cricket::PROTO_UDP: return PHASE_RELAY; + case cricket::PROTO_TCP: return PHASE_TCP; + case cricket::PROTO_SSLTCP: return PHASE_SSLTCP; + default: assert(false); + } + } else { + assert(false); + } + } else { + assert(false); + } + return PHASE_UDP; // reached only with assert failure +} + +const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds +const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds + +int ShakeDelay() { + int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1; + return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range; +} + +} + +namespace cricket { + +// Performs the allocation of ports, in a sequenced (timed) manner, for a given +// network and IP address. +class AllocationSequence : public talk_base::MessageHandler { +public: + AllocationSequence(BasicPortAllocatorSession* session, + talk_base::Network* network, + PortConfiguration* config); + ~AllocationSequence(); + + // Determines whether this sequence is operating on an equivalent network + // setup to the one given. + bool IsEquivalent(talk_base::Network* network); + + // Starts and stops the sequence. When started, it will continue allocating + // new ports on its own timed schedule. + void Start(); + void Stop(); + + // MessageHandler: + void OnMessage(talk_base::Message* msg); + + void EnableProtocol(ProtocolType proto); + bool ProtocolEnabled(ProtocolType proto) const; + +private: + BasicPortAllocatorSession* session_; + talk_base::Network* network_; + uint32 ip_; + PortConfiguration* config_; + bool running_; + int step_; + int step_of_phase_[kNumPhases]; + + typedef std::vector<ProtocolType> ProtocolList; + ProtocolList protocols_; + + void CreateUDPPorts(); + void CreateTCPPorts(); + void CreateStunPorts(); + void CreateRelayPorts(); +}; + + +// BasicPortAllocator + +BasicPortAllocator::BasicPortAllocator( + talk_base::NetworkManager* network_manager) + : network_manager_(network_manager), best_writable_phase_(-1), + stun_address_(NULL), relay_address_(NULL) { +} + +BasicPortAllocator::BasicPortAllocator( + talk_base::NetworkManager* network_manager, + talk_base::SocketAddress* stun_address, + talk_base::SocketAddress *relay_address) + : network_manager_(network_manager), best_writable_phase_(-1), + stun_address_(stun_address), relay_address_(relay_address) { +} + +BasicPortAllocator::~BasicPortAllocator() { +} + +int BasicPortAllocator::best_writable_phase() const { + // If we are configured with an HTTP proxy, the best bet is to use the relay + if ((best_writable_phase_ == -1) + && ((proxy().type == talk_base::PROXY_HTTPS) + || (proxy().type == talk_base::PROXY_UNKNOWN))) { + return PHASE_RELAY; + } + return best_writable_phase_; +} + +PortAllocatorSession *BasicPortAllocator::CreateSession( + const std::string &name, const std::string &session_type) { + return new BasicPortAllocatorSession(this, name, session_type, stun_address_, + relay_address_); +} + +void BasicPortAllocator::AddWritablePhase(int phase) { + if ((best_writable_phase_ == -1) || (phase < best_writable_phase_)) + best_writable_phase_ = phase; +} + +// BasicPortAllocatorSession + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name, + const std::string &session_type) + : PortAllocatorSession(allocator->flags()), allocator_(allocator), + name_(name), network_thread_(NULL), session_type_(session_type), + allocation_started_(false), running_(false), stun_address_(NULL), + relay_address_(NULL) { +} + +BasicPortAllocatorSession::BasicPortAllocatorSession( + BasicPortAllocator *allocator, + const std::string &name, + const std::string &session_type, + talk_base::SocketAddress *stun_address, + talk_base::SocketAddress *relay_address) + : PortAllocatorSession(allocator->flags()), allocator_(allocator), + name_(name), session_type_(session_type), network_thread_(NULL), + allocation_started_(false), running_(false), stun_address_(stun_address), + relay_address_(relay_address) { +} + +BasicPortAllocatorSession::~BasicPortAllocatorSession() { + if (network_thread_ != NULL) + network_thread_->Clear(this); + + std::vector<PortData>::iterator it; + for (it = ports_.begin(); it != ports_.end(); it++) + delete it->port; + + for (uint32 i = 0; i < configs_.size(); ++i) + delete configs_[i]; + + for (uint32 i = 0; i < sequences_.size(); ++i) + delete sequences_[i]; +} + +void BasicPortAllocatorSession::GetInitialPorts() { + network_thread_ = talk_base::Thread::Current(); + + network_thread_->Post(this, MSG_CONFIG_START); + + if (flags() & PORTALLOCATOR_ENABLE_SHAKER) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +void BasicPortAllocatorSession::StartGetAllPorts() { + assert(talk_base::Thread::Current() == network_thread_); + running_ = true; + if (allocation_started_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Start(); + for (size_t i = 0; i < ports_.size(); ++i) + ports_[i].port->Start(); +} + +void BasicPortAllocatorSession::StopGetAllPorts() { + assert(talk_base::Thread::Current() == network_thread_); + running_ = false; + network_thread_->Clear(this, MSG_ALLOCATE); + for (uint32 i = 0; i < sequences_.size(); ++i) + sequences_[i]->Stop(); +} + +void BasicPortAllocatorSession::OnMessage(talk_base::Message *message) { + switch (message->message_id) { + case MSG_CONFIG_START: + assert(talk_base::Thread::Current() == network_thread_); + GetPortConfigurations(); + break; + + case MSG_CONFIG_READY: + assert(talk_base::Thread::Current() == network_thread_); + OnConfigReady(static_cast<PortConfiguration*>(message->pdata)); + break; + + case MSG_ALLOCATE: + assert(talk_base::Thread::Current() == network_thread_); + OnAllocate(); + break; + + case MSG_SHAKE: + assert(talk_base::Thread::Current() == network_thread_); + OnShake(); + break; + + default: + assert(false); + } +} + +void BasicPortAllocatorSession::GetPortConfigurations() { + PortConfiguration* config = NULL; + if (stun_address_ != NULL) + config = new PortConfiguration(*stun_address_, + CreateRandomString(16), + CreateRandomString(16), + ""); + PortConfiguration::PortList ports; + if (relay_address_ != NULL) { + ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP)); + config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER); + } + + ConfigReady(config); +} + +void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) { + network_thread_->Post(this, MSG_CONFIG_READY, config); +} + +// Adds a configuration to the list. +void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) { + if (config) + configs_.push_back(config); + + AllocatePorts(); +} + +void BasicPortAllocatorSession::AllocatePorts() { + assert(talk_base::Thread::Current() == network_thread_); + + if (allocator_->proxy().type != talk_base::PROXY_NONE) + Port::set_proxy(allocator_->user_agent(), allocator_->proxy()); + + network_thread_->Post(this, MSG_ALLOCATE); +} + +// For each network, see if we have a sequence that covers it already. If not, +// create a new sequence to create the appropriate ports. +void BasicPortAllocatorSession::OnAllocate() { + std::vector<talk_base::Network*> networks; + allocator_->network_manager()->GetNetworks(networks); + + for (uint32 i = 0; i < networks.size(); ++i) { + if (HasEquivalentSequence(networks[i])) + continue; + + PortConfiguration* config = NULL; + if (configs_.size() > 0) + config = configs_.back(); + + AllocationSequence* sequence = + new AllocationSequence(this, networks[i], config); + if (running_) + sequence->Start(); + + sequences_.push_back(sequence); + } + + allocation_started_ = true; + if (running_) + network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE); +} + +bool BasicPortAllocatorSession::HasEquivalentSequence( + talk_base::Network* network) { + for (uint32 i = 0; i < sequences_.size(); ++i) + if (sequences_[i]->IsEquivalent(network)) + return true; + return false; +} + +void BasicPortAllocatorSession::AddAllocatedPort(Port* port, + AllocationSequence * seq, + float pref, + bool prepare_address) { + if (!port) + return; + + port->set_name(name_); + port->set_preference(pref); + port->set_generation(generation()); + PortData data; + data.port = port; + data.sequence = seq; + data.ready = false; + ports_.push_back(data); + port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady); + port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated); + port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed); + LOG_J(LS_INFO, port) << "Added port to allocator"; + if (prepare_address) + port->PrepareAddress(); + if (running_) + port->Start(); +} + +void BasicPortAllocatorSession::OnAddressReady(Port *port) { + assert(talk_base::Thread::Current() == network_thread_); + std::vector<PortData>::iterator it + = std::find(ports_.begin(), ports_.end(), port); + assert(it != ports_.end()); + if (it->ready) + return; + it->ready = true; + SignalPortReady(this, port); + + // Only accumulate the candidates whose protocol has been enabled + std::vector<Candidate> candidates; + const std::vector<Candidate>& potentials = port->candidates(); + for (size_t i=0; i<potentials.size(); ++i) { + ProtocolType pvalue; + if (!StringToProto(potentials[i].protocol().c_str(), pvalue)) + continue; + if (it->sequence->ProtocolEnabled(pvalue)) { + candidates.push_back(potentials[i]); + } + } + if (!candidates.empty()) { + SignalCandidatesReady(this, candidates); + } +} + +void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, + ProtocolType proto) { + std::vector<Candidate> candidates; + for (std::vector<PortData>::iterator it = ports_.begin(); it != ports_.end(); ++it) { + if (!it->ready || (it->sequence != seq)) + continue; + + const std::vector<Candidate>& potentials = it->port->candidates(); + for (size_t i=0; i<potentials.size(); ++i) { + ProtocolType pvalue; + if (!StringToProto(potentials[i].protocol().c_str(), pvalue)) + continue; + if (pvalue == proto) { + candidates.push_back(potentials[i]); + } + } + } + if (!candidates.empty()) { + SignalCandidatesReady(this, candidates); + } +} + +void BasicPortAllocatorSession::OnPortDestroyed(Port* port) { + assert(talk_base::Thread::Current() == network_thread_); + std::vector<PortData>::iterator iter = + find(ports_.begin(), ports_.end(), port); + assert(iter != ports_.end()); + ports_.erase(iter); + + LOG_J(LS_INFO, port) << "Removed port from allocator (" + << static_cast<int>(ports_.size()) << " remaining)"; +} + +void BasicPortAllocatorSession::OnConnectionCreated(Port* port, + Connection* conn) { + conn->SignalStateChange.connect(this, + &BasicPortAllocatorSession::OnConnectionStateChange); +} + +void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) { + if (conn->write_state() == Connection::STATE_WRITABLE) + allocator_->AddWritablePhase( + LocalCandidateToPhase(conn->local_candidate())); +} + +void BasicPortAllocatorSession::OnShake() { + LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<"; + + std::vector<Port*> ports; + std::vector<Connection*> connections; + + for (size_t i = 0; i < ports_.size(); ++i) { + if (ports_[i].ready) + ports.push_back(ports_[i].port); + } + + for (size_t i = 0; i < ports.size(); ++i) { + Port::AddressMap::const_iterator iter; + for (iter = ports[i]->connections().begin(); + iter != ports[i]->connections().end(); + ++iter) { + connections.push_back(iter->second); + } + } + + LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and " + << (int)connections.size() << " connections"; + + for (size_t i = 0; i < connections.size(); ++i) + connections[i]->Destroy(); + + if (running_ || (ports.size() > 0) || (connections.size() > 0)) + network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE); +} + +// AllocationSequence + +AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, + talk_base::Network* network, + PortConfiguration* config) + : session_(session), network_(network), ip_(network->ip()), config_(config), + running_(false), step_(0) { + + // All of the phases up until the best-writable phase so far run in step 0. + // The other phases follow sequentially in the steps after that. If there is + // no best-writable so far, then only phase 0 occurs in step 0. + int last_phase_in_step_zero = + talk_base::_max(0, session->allocator()->best_writable_phase()); + for (int phase = 0; phase < kNumPhases; ++phase) + step_of_phase_[phase] = talk_base::_max(0, phase - last_phase_in_step_zero); + + // Immediately perform phase 0. + OnMessage(NULL); +} + +AllocationSequence::~AllocationSequence() { + session_->network_thread()->Clear(this); +} + +bool AllocationSequence::IsEquivalent(talk_base::Network* network) { + return (network == network_) && (ip_ == network->ip()); +} + +void AllocationSequence::Start() { + running_ = true; + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::Stop() { + running_ = false; + session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE); +} + +void AllocationSequence::OnMessage(talk_base::Message* msg) { + assert(talk_base::Thread::Current() == session_->network_thread()); + if (msg) + assert(msg->message_id == MSG_ALLOCATION_PHASE); + + const char* const PHASE_NAMES[kNumPhases] = { + "Udp", "Relay", "Tcp", "SslTcp" + }; + + // Perform all of the phases in the current step. + for (int phase = 0; phase < kNumPhases; phase++) { + if (step_of_phase_[phase] != step_) + continue; + + LOG_J(LS_INFO, network_) << "Allocation Phase=" << PHASE_NAMES[phase] + << " (Step=" << step_ << ")"; + + switch (phase) { + case PHASE_UDP: + CreateUDPPorts(); + CreateStunPorts(); + EnableProtocol(PROTO_UDP); + break; + + case PHASE_RELAY: + CreateRelayPorts(); + break; + + case PHASE_TCP: + CreateTCPPorts(); + EnableProtocol(PROTO_TCP); + break; + + case PHASE_SSLTCP: + EnableProtocol(PROTO_SSLTCP); + break; + + default: + ASSERT(false); + } + } + + // TODO: use different delays for each stage + step_ += 1; + if (running_) { + session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY, + this, + MSG_ALLOCATION_PHASE); + } +} + +void AllocationSequence::EnableProtocol(ProtocolType proto) { + if (!ProtocolEnabled(proto)) { + protocols_.push_back(proto); + session_->OnProtocolEnabled(this, proto); + } +} + +bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const { + for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) { + if (*it == proto) + return true; + } + return false; +} + +void AllocationSequence::CreateUDPPorts() { + if (session_->flags() & PORTALLOCATOR_DISABLE_UDP) + return; + + Port* port = new UDPPort(session_->network_thread(), NULL, network_, + talk_base::SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP); +} + +void AllocationSequence::CreateTCPPorts() { + if (session_->flags() & PORTALLOCATOR_DISABLE_TCP) + return; + + Port* port = new TCPPort(session_->network_thread(), NULL, network_, + talk_base::SocketAddress(ip_, 0)); + session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP); +} + +void AllocationSequence::CreateStunPorts() { + if (session_->flags() & PORTALLOCATOR_DISABLE_STUN) + return; + + if (!config_ || config_->stun_address.IsAny()) + return; + + Port* port = new StunPort(session_->network_thread(), NULL, network_, + talk_base::SocketAddress(ip_, 0), + config_->stun_address); + session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN); +} + +void AllocationSequence::CreateRelayPorts() { + if (session_->flags() & PORTALLOCATOR_DISABLE_RELAY) + return; + + if (!config_) + return; + + PortConfiguration::RelayList::const_iterator relay; + for (relay = config_->relays.begin(); + relay != config_->relays.end(); + ++relay) { + + RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_, + talk_base::SocketAddress(ip_, 0), + config_->username, config_->password, + config_->magic_cookie); + // Note: We must add the allocated port before we add addresses because + // the latter will create candidates that need name and preference + // settings. However, we also can't prepare the address (normally + // done by AddAllocatedPort) until we have these addresses. So we + // wait to do that until below. + session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, + false); + + // Add the addresses of this protocol. + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay->ports.begin(); + relay_port != relay->ports.end(); + ++relay_port) { + port->AddServerAddress(*relay_port); + port->AddExternalAddress(*relay_port); + } + + // Start fetching an address for this port. + port->PrepareAddress(); + } +} + +// PortConfiguration + +PortConfiguration::PortConfiguration(const talk_base::SocketAddress& sa, + const std::string& un, + const std::string& pw, + const std::string& mc) + : stun_address(sa), username(un), password(pw), magic_cookie(mc) { +} + +void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) { + RelayServer relay; + relay.ports = ports; + relay.pref_modifier = pref_modifier; + relays.push_back(relay); +} + +bool PortConfiguration::SupportsProtocol( + const PortConfiguration::RelayServer& relay, ProtocolType type) { + PortConfiguration::PortList::const_iterator relay_port; + for (relay_port = relay.ports.begin(); + relay_port != relay.ports.end(); + ++relay_port) { + if (relay_port->proto == type) + return true; + } + return false; +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h new file mode 100644 index 0000000..0e3d313 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/basicportallocator.h @@ -0,0 +1,175 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _BASICPORTALLOCATOR_H_ +#define _BASICPORTALLOCATOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/messagequeue.h" +#include "talk/base/network.h" +#include "talk/p2p/base/portallocator.h" +#include <string> +#include <vector> + +namespace cricket { + +class BasicPortAllocator : public PortAllocator { +public: + BasicPortAllocator(talk_base::NetworkManager* network_manager); + BasicPortAllocator(talk_base::NetworkManager* network_manager, + talk_base::SocketAddress *stun_server, talk_base::SocketAddress *relay_server); + virtual ~BasicPortAllocator(); + + talk_base::NetworkManager* network_manager() { return network_manager_; } + + // Returns the best (highest preference) phase that has produced a port that + // produced a writable connection. If no writable connections have been + // produced, this returns -1. + int best_writable_phase() const; + + virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type); + + // Called whenever a connection becomes writable with the argument being the + // phase that the corresponding port was created in. + void AddWritablePhase(int phase); + +private: + talk_base::NetworkManager* network_manager_; + talk_base::SocketAddress* stun_address_; + talk_base::SocketAddress* relay_address_; + int best_writable_phase_; +}; + +struct PortConfiguration; +class AllocationSequence; + +class BasicPortAllocatorSession: public PortAllocatorSession, + public talk_base::MessageHandler { +public: + BasicPortAllocatorSession(BasicPortAllocator *allocator, + const std::string &name, + const std::string &session_type); + BasicPortAllocatorSession(BasicPortAllocator *allocator, + const std::string &name, + const std::string &session_type, + talk_base::SocketAddress *stun_address, + talk_base::SocketAddress *relay_address); + ~BasicPortAllocatorSession(); + + BasicPortAllocator* allocator() { return allocator_; } + const std::string& name() const { return name_; } + const std::string& session_type() const { return session_type_; } + talk_base::Thread* network_thread() { return network_thread_; } + + virtual void GetInitialPorts(); + virtual void StartGetAllPorts(); + virtual void StopGetAllPorts(); + virtual bool IsGettingAllPorts() { return running_; } + +protected: + // Starts the process of getting the port configurations. + virtual void GetPortConfigurations(); + + // Adds a port configuration that is now ready. Once we have one for each + // network (or a timeout occurs), we will start allocating ports. + void ConfigReady(PortConfiguration* config); + + // MessageHandler. Can be overriden if message IDs do not conflict. + virtual void OnMessage(talk_base::Message *message); + +private: + void OnConfigReady(PortConfiguration* config); + void OnConfigTimeout(); + void AllocatePorts(); + void OnAllocate(); + bool HasEquivalentSequence(talk_base::Network* network); + void AddAllocatedPort(Port* port, AllocationSequence * seq, float pref, + bool prepare_address = true); + void OnAddressReady(Port *port); + void OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto); + void OnPortDestroyed(Port* port); + void OnConnectionCreated(Port* port, Connection* conn); + void OnConnectionStateChange(Connection* conn); + void OnShake(); + + BasicPortAllocator *allocator_; + std::string name_; + std::string session_type_; + talk_base::Thread* network_thread_; + bool configuration_done_; + bool allocation_started_; + bool running_; // set when StartGetAllPorts is called + std::vector<PortConfiguration*> configs_; + std::vector<AllocationSequence*> sequences_; + talk_base::SocketAddress *stun_address_; + talk_base::SocketAddress *relay_address_; + + struct PortData { + Port * port; + AllocationSequence * sequence; + bool ready; + + bool operator==(Port * rhs) const { return (port == rhs); } + }; + std::vector<PortData> ports_; + + friend class AllocationSequence; +}; + +// Records configuration information useful in creating ports. +struct PortConfiguration : public talk_base::MessageData { + talk_base::SocketAddress stun_address; + std::string username; + std::string password; + std::string magic_cookie; + + typedef std::vector<ProtocolAddress> PortList; + struct RelayServer { + PortList ports; + float pref_modifier; // added to the protocol modifier to get the + // preference for this particular server + }; + + typedef std::vector<RelayServer> RelayList; + RelayList relays; + + PortConfiguration(const talk_base::SocketAddress& stun_address, + const std::string& username, + const std::string& password, + const std::string& magic_cookie); + + // Adds another relay server, with the given ports and modifier, to the list. + void AddRelay(const PortList& ports, float pref_modifier); + + // Determines whether the given relay server supports the given protocol. + static bool SupportsProtocol(const PortConfiguration::RelayServer& relay, + ProtocolType type); +}; + +} // namespace cricket + +#endif // _BASICPORTALLOCATOR_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc new file mode 100644 index 0000000..9837478 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.cc @@ -0,0 +1,190 @@ +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma warning(disable:4786) +#endif +#include "talk/base/asynchttprequest.h" +#include "talk/base/basicdefs.h" +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/base/signalthread.h" +#include "talk/p2p/client/httpportallocator.h" +#include <cassert> +#include <ctime> + +namespace { + +// Records the port on the hosts that will receive HTTP requests. +const uint16 kHostPort = 80; + +// Records the URL that we will GET in order to create a session. +const std::string kCreateSessionURL = "/create_session"; + +// The number of HTTP requests we should attempt before giving up. +const size_t kNumRetries = 5; + +// The delay before we give up on an HTTP request; +const int TIMEOUT = 5 * 1000; // 5 seconds + +const uint32 MSG_TIMEOUT = 100; // must not conflict with BasicPortAllocator.cpp + +// Helper routine to remove whitespace from the ends of a string. +void Trim(std::string& str) { + size_t first = str.find_first_not_of(" \t\r\n"); + if (first == std::string::npos) { + str.clear(); + return; + } + + size_t last = str.find_last_not_of(" \t\r\n"); + ASSERT(last != std::string::npos); +} + +// Parses the lines in the result of the HTTP request that are of the form +// 'a=b' and returns them in a map. +typedef std::map<std::string,std::string> StringMap; +void ParseMap(const std::string& string, StringMap& map) { + size_t start_of_line = 0; + size_t end_of_line = 0; + + for (;;) { // for each line + start_of_line = string.find_first_not_of("\r\n", end_of_line); + if (start_of_line == std::string::npos) + break; + + end_of_line = string.find_first_of("\r\n", start_of_line); + if (end_of_line == std::string::npos) { + end_of_line = string.length(); + } + + size_t equals = string.find('=', start_of_line); + if ((equals >= end_of_line) || (equals == std::string::npos)) + continue; + + std::string key(string, start_of_line, equals - start_of_line); + std::string value(string, equals + 1, end_of_line - equals - 1); + + Trim(key); + Trim(value); + + if ((key.size() > 0) && (value.size() > 0)) + map[key] = value; + } +} + +} + +namespace cricket { + +// HttpPortAllocator + +HttpPortAllocator::HttpPortAllocator(talk_base::NetworkManager* network_manager, const std::string &user_agent) + : BasicPortAllocator(network_manager), agent_(user_agent) { + relay_hosts_.push_back("relay.l.google.com"); + stun_hosts_.push_back(talk_base::SocketAddress("stun.l.google.com",19302)); +} + +HttpPortAllocator::~HttpPortAllocator() { +} + +PortAllocatorSession *HttpPortAllocator::CreateSession(const std::string &name, const std::string &session_type) { + return new HttpPortAllocatorSession(this, name, session_type, stun_hosts_, relay_hosts_, relay_token_, agent_); +} + +// HttpPortAllocatorSession + +HttpPortAllocatorSession::HttpPortAllocatorSession(HttpPortAllocator* allocator, const std::string &name, + const std::string &session_type, + const std::vector<talk_base::SocketAddress> &stun_hosts, + const std::vector<std::string> &relay_hosts, + const std::string &relay_token, + const std::string &user_agent) + : BasicPortAllocatorSession(allocator, name, session_type), + attempts_(0), relay_hosts_(relay_hosts), stun_hosts_(stun_hosts), relay_token_(relay_token), agent_(user_agent) { +} + +void HttpPortAllocatorSession::GetPortConfigurations() { + + if (attempts_ == kNumRetries) { + LOG(WARNING) << "HttpPortAllocator: maximum number of requests reached"; + return; + } + + if (relay_hosts_.size() <= 0) { + LOG(WARNING) << "HttpPortAllocator: no relay hosts found"; + return; + } + + // Choose the next host to try. + std::string host = relay_hosts_[attempts_ % relay_hosts_.size()]; + attempts_++; + LOG(INFO) << "HTTPPortAllocator: sending to host " << host; + + // Initiate an HTTP request to create a session through the chosen host. + + talk_base::AsyncHttpRequest* request = new talk_base::AsyncHttpRequest(agent_); + request->SignalWorkDone.connect(this, &HttpPortAllocatorSession::OnRequestDone); + + request->set_proxy(allocator()->proxy()); + request->response().document.reset(new talk_base::MemoryStream); + request->request().verb = talk_base::HV_GET; + request->request().path = kCreateSessionURL; + request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token_, true); + request->request().addHeader("X-Google-Relay-Auth", relay_token_, true); + request->request().addHeader("X-Session-Type", session_type(), true); + request->set_host(host); + request->set_port(kHostPort); + request->Start(); + request->Release(); +} + +void HttpPortAllocatorSession::OnRequestDone(talk_base::SignalThread* data) { + talk_base::AsyncHttpRequest *request = + static_cast<talk_base::AsyncHttpRequest*> (data); + if (request->response().scode != 200) { + LOG(WARNING) << "HTTPPortAllocator: request " + << " received error " << request->response().scode; + GetPortConfigurations(); + return; + } + LOG(INFO) << "HTTPPortAllocator: request succeeded"; + + StringMap map; + talk_base::MemoryStream *stream = static_cast<talk_base::MemoryStream*>(request->response().document.get()); + stream->Rewind(); + size_t length; + stream->GetSize(&length); + std::string resp = std::string(stream->GetBuffer(), length); + ParseMap(resp, map); + + std::string username = map["username"]; + std::string password = map["password"]; + std::string magic_cookie = map["magic_cookie"]; + + std::string relay_ip = map["relay.ip"]; + std::string relay_udp_port = map["relay.udp_port"]; + std::string relay_tcp_port = map["relay.tcp_port"]; + std::string relay_ssltcp_port = map["relay.ssltcp_port"]; + + PortConfiguration* config = new PortConfiguration(stun_hosts_[0], + username, + password, + magic_cookie); + + PortConfiguration::PortList ports; + if (!relay_udp_port.empty()) { + talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str())); + ports.push_back(ProtocolAddress(address, PROTO_UDP)); + } + if (!relay_tcp_port.empty()) { + talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str())); + ports.push_back(ProtocolAddress(address, PROTO_TCP)); + } + if (!relay_ssltcp_port.empty()) { + talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str())); + ports.push_back(ProtocolAddress(address, PROTO_SSLTCP)); + } + config->AddRelay(ports, 0.0f); + ConfigReady(config); +} + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h new file mode 100644 index 0000000..96f3e72 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/httpportallocator.h @@ -0,0 +1,61 @@ +#ifndef _HTTPPORTALLOCATOR_H_ +#define _HTTPPORTALLOCATOR_H_ + +#include "talk/p2p/client/basicportallocator.h" + +namespace talk_base { + class SignalThread; +} + +namespace cricket { + +class HttpPortAllocator : public BasicPortAllocator { +public: + HttpPortAllocator(talk_base::NetworkManager* network_manager, const std::string &user_agent); + virtual ~HttpPortAllocator(); + + virtual PortAllocatorSession *CreateSession(const std::string &name, + const std::string &session_type); + void SetStunHosts(const std::vector<talk_base::SocketAddress> &hosts) {stun_hosts_ = hosts;} + void SetRelayHosts(const std::vector<std::string> &hosts) {relay_hosts_ = hosts;} + void SetRelayToken(const std::string &relay) {relay_token_ = relay;} + std::string relay_token() const { return relay_token_; } +private: + std::vector<talk_base::SocketAddress> stun_hosts_; + std::vector<std::string> relay_hosts_; + std::string relay_token_; + std::string agent_; +}; + +class RequestData; + +class HttpPortAllocatorSession : public BasicPortAllocatorSession { + public: + HttpPortAllocatorSession(HttpPortAllocator *allocator, + const std::string &name, + const std::string &session_type, + const std::vector<talk_base::SocketAddress> &stun_hosts, + const std::vector<std::string> &relay_hosts, + const std::string &relay, + const std::string &agent); + ~HttpPortAllocatorSession() {}; + +protected: + virtual void GetPortConfigurations(); + +private: + std::vector<std::string> relay_hosts_; + std::vector<talk_base::SocketAddress> stun_hosts_; + std::string relay_token_; + std::string agent_; + + void OnRequestDone(talk_base::SignalThread* request); + HttpPortAllocator* http_allocator() { + return static_cast<HttpPortAllocator*>(allocator()); + } + int attempts_; +}; + +} // namespace cricket + +#endif // _XMPPPORTALLOCATOR_H_ diff --git a/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc new file mode 100644 index 0000000..88d37ce --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.cc @@ -0,0 +1,164 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/p2p/client/socketmonitor.h" +#include "talk/base/common.h" + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +SocketMonitor::SocketMonitor(Session* session, + TransportChannel* channel, + talk_base::Thread *monitor_thread) { + session_ = session; + channel_ = channel; + channel_thread_ = session->session_manager()->worker_thread(); + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +SocketMonitor::~SocketMonitor() { + channel_thread_->Clear(this); + monitoring_thread_->Clear(this); +} + +void SocketMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 250) + rate_ = 250; + channel_thread_->Post(this, MSG_MONITOR_START); +} + +void SocketMonitor::Stop() { + channel_thread_->Post(this, MSG_MONITOR_STOP); +} + +void SocketMonitor::OnMessage(talk_base::Message *message) { + talk_base::CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + ASSERT(talk_base::Thread::Current() == channel_thread_); + if (!monitoring_) { + monitoring_ = true; + if (GetP2PChannel() != NULL) { + GetP2PChannel()->SignalConnectionMonitor.connect( + this, &SocketMonitor::OnConnectionMonitor); + } + PollSocket(true); + } + break; + + case MSG_MONITOR_STOP: + ASSERT(talk_base::Thread::Current() == channel_thread_); + if (monitoring_) { + monitoring_ = false; + if (GetP2PChannel() != NULL) + GetP2PChannel()->SignalConnectionMonitor.disconnect(this); + channel_thread_->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + ASSERT(talk_base::Thread::Current() == channel_thread_); + PollSocket(true); + break; + + case MSG_MONITOR_SIGNAL: + { + ASSERT(talk_base::Thread::Current() == monitoring_thread_); + std::vector<ConnectionInfo> infos = connection_infos_; + crit_.Leave(); + SignalUpdate(this, infos); + crit_.Enter(); + } + break; + } +} + +void SocketMonitor::OnConnectionMonitor(P2PTransportChannel* channel) { + talk_base::CritScope cs(&crit_); + if (monitoring_) + PollSocket(false); +} + +void SocketMonitor::PollSocket(bool poll) { + ASSERT(talk_base::Thread::Current() == channel_thread_); + talk_base::CritScope cs(&crit_); + + // Gather connection infos + + P2PTransportChannel* p2p_channel = GetP2PChannel(); + if (p2p_channel != NULL) { + connection_infos_.clear(); + const std::vector<Connection *> &connections = p2p_channel->connections(); + std::vector<Connection *>::const_iterator it; + for (it = connections.begin(); it != connections.end(); it++) { + Connection *connection = *it; + ConnectionInfo info; + info.best_connection = p2p_channel->best_connection() == connection; + info.readable = connection->read_state() == Connection::STATE_READABLE; + info.writable = connection->write_state() == Connection::STATE_WRITABLE; + info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT; + info.new_connection = !connection->reported(); + connection->set_reported(true); + info.rtt = connection->rtt(); + info.sent_total_bytes = connection->sent_total_bytes(); + info.sent_bytes_second = connection->sent_bytes_second(); + info.recv_total_bytes = connection->recv_total_bytes(); + info.recv_bytes_second = connection->recv_bytes_second(); + info.local_candidate = connection->local_candidate(); + info.remote_candidate = connection->remote_candidate(); + info.est_quality = connection->port()->network()->quality(); + info.key = reinterpret_cast<void *>(connection); + connection_infos_.push_back(info); + } + } + + // Signal the monitoring thread, start another poll timer + + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + if (poll) + channel_thread_->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +P2PTransportChannel* SocketMonitor::GetP2PChannel() { + if (session_->transport() == NULL) + return NULL; + if (session_->transport()->name() != kNsP2pTransport) + return NULL; + TransportChannelImpl* impl = session_->GetImplementation(channel_); + if (impl == NULL) + return NULL; + return static_cast<P2PTransportChannel*>(impl); +} + +} diff --git a/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h new file mode 100644 index 0000000..ced86b8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/p2p/client/socketmonitor.h @@ -0,0 +1,91 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SOCKETMONITOR_H_ +#define _SOCKETMONITOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/sigslot.h" +#include "talk/base/criticalsection.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/p2ptransportchannel.h" +#include "talk/p2p/base/port.h" +#include <vector> + +namespace cricket { + +struct ConnectionInfo { + bool best_connection; + bool writable; + bool readable; + bool timeout; + bool new_connection; + size_t rtt; + size_t sent_total_bytes; + size_t sent_bytes_second; + size_t recv_total_bytes; + size_t recv_bytes_second; + Candidate local_candidate; + Candidate remote_candidate; + double est_quality; + void *key; +}; + +class SocketMonitor : public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + SocketMonitor(Session* session, TransportChannel* channel, + talk_base::Thread *monitor_thread); + ~SocketMonitor(); + + void Start(int cms); + void Stop(); + + talk_base::Thread *monitor_thread() { return monitoring_thread_; } + + sigslot::signal2<SocketMonitor *, + const std::vector<ConnectionInfo> &> SignalUpdate; + +protected: + void OnMessage(talk_base::Message *message); + void OnConnectionMonitor(P2PTransportChannel* channel); + void PollSocket(bool poll); + P2PTransportChannel* GetP2PChannel(); + + std::vector<ConnectionInfo> connection_infos_; + Session* session_; + TransportChannel* channel_; + talk_base::Thread* channel_thread_; + talk_base::Thread* monitoring_thread_; + talk_base::CriticalSection crit_; + uint32 rate_; + bool monitoring_; +}; + +} + +#endif // _SOCKETMONITOR_H_ diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am new file mode 100644 index 0000000..a6eebf6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am @@ -0,0 +1,7 @@ +libcricketsessionfileshare_la_SOURCES = fileshare.cc + +noinst_HEADERS = fileshare.h + +AM_CPPFLAGS := -DPOSIX +noinst_LTLIBRARIES = libcricketsessionfileshare.la + diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in new file mode 100644 index 0000000..3c9cd69 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in @@ -0,0 +1,446 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = talk/session/fileshare +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcricketsessionfileshare_la_LIBADD = +am_libcricketsessionfileshare_la_OBJECTS = fileshare.lo +libcricketsessionfileshare_la_OBJECTS = \ + $(am_libcricketsessionfileshare_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libcricketsessionfileshare_la_SOURCES) +DIST_SOURCES = $(libcricketsessionfileshare_la_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +libcricketsessionfileshare_la_SOURCES = fileshare.cc +noinst_HEADERS = fileshare.h +AM_CPPFLAGS := -DPOSIX +noinst_LTLIBRARIES = libcricketsessionfileshare.la +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/fileshare/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/session/fileshare/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcricketsessionfileshare.la: $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_DEPENDENCIES) + $(CXXLINK) $(libcricketsessionfileshare_la_LDFLAGS) $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileshare.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc new file mode 100644 index 0000000..863c352 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc @@ -0,0 +1,1313 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/session/fileshare/fileshare.h" + +#include "talk/base/httpcommon-inl.h" + +#include "talk/base/fileutils.h" +#include "talk/base/streamutils.h" +#include "talk/base/event.h" +#include "talk/base/helpers.h" +#include "talk/base/httpclient.h" +#include "talk/base/httpserver.h" +#include "talk/base/pathutils.h" +#include "talk/base/socketstream.h" +#include "talk/base/stringdigest.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" +#include "talk/base/tarstream.h" +#include "talk/base/thread.h" +#include "talk/session/tunnel/pseudotcpchannel.h" +#include "talk/session/tunnel/tunnelsessionclient.h" + +/////////////////////////////////////////////////////////////////////////////// +// <description xmlns="http://www.google.com/session/share"> +// <manifest> +// <file size='341'> +// <name>foo.txt</name> +// </file> +// <file size='51321'> +// <name>foo.jpg</name> +// <image width='480' height='320'/> +// </file> +// <folder> +// <name>stuff</name> +// </folder> +// </manifest> +// <protocol> +// <http> +// <url name='source-path'>/temporary/23A53F01/</url> +// <url name='preview-path'>/temporary/90266EA1/</url> +// </http> +// <raw/> +// </protocol> +// </description> +// <p:transport xmns:p="p2p"/> +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Constants and private functions +/////////////////////////////////////////////////////////////////////////////// + +const std::string NS_GOOGLE_SHARE("http://www.google.com/session/share"); + +namespace { + +const buzz::QName QN_SHARE_DESCRIPTION(true, NS_GOOGLE_SHARE, "description"); +const buzz::QName QN_SHARE_MANIFEST(true, NS_GOOGLE_SHARE, "manifest"); +const buzz::QName QN_SHARE_FOLDER(true, NS_GOOGLE_SHARE, "folder"); +const buzz::QName QN_SHARE_FILE(true, NS_GOOGLE_SHARE, "file"); +const buzz::QName QN_SHARE_NAME(true, NS_GOOGLE_SHARE, "name"); +const buzz::QName QN_SHARE_IMAGE(true, NS_GOOGLE_SHARE, "image"); +const buzz::QName QN_SHARE_PROTOCOL(true, NS_GOOGLE_SHARE, "protocol"); +const buzz::QName QN_SHARE_HTTP(true, NS_GOOGLE_SHARE, "http"); +const buzz::QName QN_SHARE_URL(true, NS_GOOGLE_SHARE, "url"); +const buzz::QName QN_SHARE_CHANNEL(true, NS_GOOGLE_SHARE, "channel"); +const buzz::QName QN_SHARE_COMPLETE(true, NS_GOOGLE_SHARE, "complete"); + +const buzz::QName QN_SIZE(true, buzz::STR_EMPTY, "size"); +const buzz::QName QN_WIDTH(true, buzz::STR_EMPTY, "width"); +const buzz::QName QN_HEIGHT(true, buzz::STR_EMPTY, "height"); + +const std::string kHttpSourcePath("source-path"); +const std::string kHttpPreviewPath("preview-path"); + +const size_t kMinImageSize = 16U; +const size_t kMaxImageSize = 0x8000U; // (32k) +const size_t kMaxPreviewSize = 1024; +// Wait 10 seconds to see if any new proxies get established +const uint32 kProxyWait = 10000; + +const int MSG_RETRY = 1; +const uint32 kFileTransferEnableRetryMs = 1000 * 60 * 4; // 4 minutes + +const std::string MIME_OCTET_STREAM("application/octet-stream"); + +enum { + MSG_PROXY_WAIT, +}; + +bool AllowedImageDimensions(size_t width, size_t height) { + return (width >= kMinImageSize) && (width <= kMaxImageSize) + && (height >= kMinImageSize) && (height <= kMaxImageSize); +} + +} // anon namespace + +namespace cricket { + +/////////////////////////////////////////////////////////////////////////////// +// FileShareManifest +/////////////////////////////////////////////////////////////////////////////// + +void +FileShareManifest::AddFile(const std::string& name, size_t size) { + Item i = { T_FILE, name, size }; + items_.push_back(i); +} + +void +FileShareManifest::AddImage(const std::string& name, size_t size, + size_t width, size_t height) { + Item i = { T_IMAGE, name, size, width, height }; + items_.push_back(i); +} + +void +FileShareManifest::AddFolder(const std::string& name, size_t size) { + Item i = { T_FOLDER, name, size }; + items_.push_back(i); +} + +size_t +FileShareManifest::GetItemCount(Type t) const { + size_t count = 0; + for (size_t i=0; i<items_.size(); ++i) { + if (items_[i].type == t) + ++count; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +// FileShareSession +/////////////////////////////////////////////////////////////////////////////// + +FileShareSession::FileShareSession(cricket::Session* session, const std::string &user_agent) + : session_(session), state_(FS_NONE), + is_closed_(false), + is_sender_(false), manifest_(NULL), pool_(this), http_client_(NULL), + http_server_(NULL), + transfer_connection_id_(talk_base::HTTP_INVALID_CONNECTION_ID), + counter_(NULL), item_transferring_(0), bytes_transferred_(0), + local_cancel_(false), local_listener_(NULL), remote_listener_(NULL), + next_channel_id_(1), user_agent_(user_agent) { + session_->SignalState.connect(this, &FileShareSession::OnSessionState); + session_->SignalInfoMessage.connect(this, + &FileShareSession::OnSessionInfoMessage); + session_->SignalChannelGone.connect(this, + &FileShareSession::OnSessionChannelGone); +} + +FileShareSession::~FileShareSession() { + ASSERT(FS_NONE != state_); + // If we haven't closed, do cleanup now. + if (!IsClosed()) { + if (!IsComplete()) { + state_ = FS_FAILURE; + } + DoClose(true); + } + if (session_) { + // Make sure we don't get future state changes on this session. + session_->SignalState.disconnect(this); + session_->SignalInfoMessage.disconnect(this); + session_ = NULL; + } + + for (TransactionList::const_iterator trans_it = transactions_.begin(); + trans_it != transactions_.end(); ++trans_it) { + (*trans_it)->response()->set_error(talk_base::HC_NOT_FOUND); + http_server_->Respond(*trans_it); + } + + delete http_client_; + delete http_server_; + delete manifest_; + delete local_listener_; + delete remote_listener_; +} + +bool +FileShareSession::IsComplete() const { + return (state_ >= FS_COMPLETE); +} + +bool +FileShareSession::IsClosed() const { + return is_closed_; +} + +FileShareState +FileShareSession::state() const { + return state_; +} + +bool +FileShareSession::is_sender() const { + ASSERT(FS_NONE != state_); + return is_sender_; +} + +const buzz::Jid& +FileShareSession::jid() const { + ASSERT(FS_NONE != state_); + return jid_; +} + +const FileShareManifest* +FileShareSession::manifest() const { + ASSERT(FS_NONE != state_); + return manifest_; +} + +const std::string& +FileShareSession::local_folder() const { + ASSERT(!local_folder_.empty()); + return local_folder_; +} + +void +FileShareSession::Share(const buzz::Jid& jid, FileShareManifest* manifest) { + ASSERT(FS_NONE == state_); + ASSERT(NULL != session_); + + http_server_ = new talk_base::HttpServer; + http_server_->SignalHttpRequest.connect(this, + &FileShareSession::OnHttpRequest); + http_server_->SignalHttpRequestComplete.connect(this, + &FileShareSession::OnHttpRequestComplete); + http_server_->SignalConnectionClosed.connect(this, + &FileShareSession::OnHttpConnectionClosed); + + FileShareDescription* desc = new FileShareDescription; + desc->supports_http = true; + desc->manifest = *manifest; + GenerateTemporaryPrefix(&desc->source_path); + GenerateTemporaryPrefix(&desc->preview_path); + session_->Initiate(jid.Str(), NULL, desc); + + delete manifest; +} + +void +FileShareSession::Accept() { + ASSERT(FS_OFFER == state_); + ASSERT(NULL != session_); + ASSERT(NULL != manifest_); + + ASSERT(!http_client_); + ASSERT(item_transferring_ == 0); + http_client_ = new talk_base::HttpClient(user_agent_, + &pool_); + http_client_->SignalHttpClientComplete.connect(this, + &FileShareSession::OnHttpClientComplete); + http_client_->SignalHttpClientClosed.connect(this, + &FileShareSession::OnHttpClientClosed); + + // The receiver now has a need for the http_server_, when previewing already + // downloaded content. + http_server_ = new talk_base::HttpServer; + http_server_->SignalHttpRequest.connect(this, + &FileShareSession::OnHttpRequest); + http_server_->SignalHttpRequestComplete.connect(this, + &FileShareSession::OnHttpRequestComplete); + http_server_->SignalConnectionClosed.connect(this, + &FileShareSession::OnHttpConnectionClosed); + + FileShareDescription* desc = new FileShareDescription; + desc->supports_http = description()->supports_http; + session_->Accept(desc); + + SetState(FS_TRANSFER, false); + NextDownload(); +} + +void +FileShareSession::Decline() { + ASSERT(FS_OFFER == state_); + ASSERT(NULL != session_); + local_cancel_ = true; + session_->Reject(); +} + +void +FileShareSession::Cancel() { + ASSERT(!IsComplete()); + ASSERT(NULL != session_); + local_cancel_ = true; + session_->Terminate(); +} + +bool +FileShareSession::GetItemUrl(size_t index, std::string* url) { + return GetItemBaseUrl(index, false, url); +} + +bool FileShareSession::GetImagePreviewUrl(size_t index, size_t width, + size_t height, std::string* url) { + if (!GetItemBaseUrl(index, true, url)) + return false; + + if (FileShareManifest::T_IMAGE != manifest_->item(index).type) { + ASSERT(false); + return false; + } + + char query[256]; + talk_base::sprintfn(query, ARRAY_SIZE(query), "?width=%u&height=%u", + width, height); + url->append(query); + return true; +} + +void FileShareSession::ResampleComplete(talk_base::StreamInterface *i, talk_base::HttpTransaction *trans, bool success) { + bool found = false; + for (TransactionList::const_iterator trans_it = transactions_.begin(); + trans_it != transactions_.end(); ++trans_it) { + if (*trans_it == trans) { + found = true; + break; + } + } + + if (!found) + return; + + transactions_.remove(trans); + + if (success) { + trans->response()->set_success(MIME_OCTET_STREAM, i); + http_server_->Respond(trans); + + } + trans->response()->set_error(talk_base::HC_NOT_FOUND); + http_server_->Respond(trans); +} + +bool FileShareSession::GetProgress(size_t& bytes) const { + bool known = true; + bytes = bytes_transferred_; + if (counter_) { + size_t current_size = manifest_->item(item_transferring_).size; + size_t current_pos = counter_->GetByteCount(); + if (current_size == FileShareManifest::SIZE_UNKNOWN) { + known = false; + } else if (current_pos > current_size) { + // Don't allow the size of a 'known' item to be reported as larger than + // it claimed to be. + ASSERT(false); + current_pos = current_size; + } + bytes += current_pos; + } + return known; +} + +bool FileShareSession::GetTotalSize(size_t& bytes) const { + bool known = true; + bytes = 0; + for (size_t i=0; i<manifest_->size(); ++i) { + if (manifest_->item(i).size == FileShareManifest::SIZE_UNKNOWN) { + // We make files of unknown length worth a single byte. + known = false; + bytes += 1; + } else { + bytes += manifest_->item(i).size; + } + } + return known; +} + +bool FileShareSession::GetCurrentItemName(std::string* name) { + if (FS_TRANSFER != state_) { + name->clear(); + return false; + } + ASSERT(item_transferring_ < manifest_->size()); + if (transfer_name_.empty()) { + const FileShareManifest::Item& item = manifest_->item(item_transferring_); + *name = item.name; + } else { + *name = transfer_name_; + } + return !name->empty(); +} + +// StreamPool Implementation + +talk_base::StreamInterface* FileShareSession::RequestConnectedStream( + const talk_base::SocketAddress& remote, int* err) { + ASSERT(remote.IPAsString() == jid_.Str()); + ASSERT(!IsClosed()); + ASSERT(NULL != session_); + if (!session_) { + if (err) + *err = -1; + return NULL; + } + + char channel_name[64]; + talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name), + "private-%u", next_channel_id_++); + if (err) + *err = 0; + return CreateChannel(channel_name); +} + +void FileShareSession::ReturnConnectedStream( + talk_base::StreamInterface* stream) { + talk_base::Thread::Current()->Dispose(stream); +} + +// MessageHandler Implementation + +void FileShareSession::OnMessage(talk_base::Message* msg) { + if (MSG_PROXY_WAIT == msg->message_id) { + LOG_F(LS_INFO) << "MSG_PROXY_WAIT"; + if (proxies_.empty() && IsComplete() && !IsClosed()) { + DoClose(true); + } + } +} + +// Session Signals + +void FileShareSession::OnSessionState(cricket::Session* session, + cricket::Session::State state) { + // Once we are complete, state changes are meaningless. + if (!IsComplete()) { + switch (state) { + case cricket::Session::STATE_SENTINITIATE: + case cricket::Session::STATE_RECEIVEDINITIATE: + OnInitiate(); + break; + case cricket::Session::STATE_SENTACCEPT: + case cricket::Session::STATE_RECEIVEDACCEPT: + case cricket::Session::STATE_INPROGRESS: + SetState(FS_TRANSFER, false); + break; + case cricket::Session::STATE_SENTREJECT: + case cricket::Session::STATE_SENTTERMINATE: + case cricket::Session::STATE_DEINIT: + if (local_cancel_) { + SetState(FS_LOCAL_CANCEL, false); + } else { + SetState(FS_REMOTE_CANCEL, false); + } + break; + case cricket::Session::STATE_RECEIVEDTERMINATE: + if (is_sender()) { + // If we are the sender, and the receiver downloaded the correct number + // of bytes, then we assume the transfer was successful. We've + // introduced support for explicit completion notification + // (QN_SHARE_COMPLETE), but it's not mandatory at this point, so we need + // this as a fallback. + size_t total_bytes; + GetTotalSize(total_bytes); + if (bytes_transferred_ >= total_bytes) { + SetState(FS_COMPLETE, false); + break; + } + } + // Fall through + case cricket::Session::STATE_RECEIVEDREJECT: + SetState(FS_REMOTE_CANCEL, false); + break; + case cricket::Session::STATE_INIT: + case cricket::Session::STATE_SENTMODIFY: + case cricket::Session::STATE_RECEIVEDMODIFY: + case cricket::Session::STATE_SENTREDIRECT: + default: + // These states should not occur. + ASSERT(false); + break; + } + } + + if (state == cricket::Session::STATE_DEINIT) { + if (!IsClosed()) { + DoClose(false); + } + session_ = NULL; + } +} + +void FileShareSession::OnSessionInfoMessage(cricket::Session* session, + const cricket::Session::XmlElements& els) { + if (IsClosed()) + return; + ASSERT(NULL != session_); + for (size_t i=0; i<els.size(); ++i) { + if (is_sender() && (els[i]->Name() == QN_SHARE_CHANNEL)) { + if (els[i]->HasAttr(buzz::QN_NAME)) { + cricket::PseudoTcpChannel* channel = + new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_); + VERIFY(channel->Connect(els[i]->Attr(buzz::QN_NAME))); + talk_base::StreamInterface* stream = channel->GetStream(); + http_server_->HandleConnection(stream); + } + } else if (is_sender() && (els[i]->Name() == QN_SHARE_COMPLETE)) { + // Normal file transfer has completed, but receiver may still be getting + // previews. + if (!IsComplete()) { + SetState(FS_COMPLETE, true); + } + } else { + LOG(LS_WARNING) << "Unknown FileShareSession info message: " + << els[i]->Name().Merged(); + } + } +} + +void FileShareSession::OnSessionChannelGone(cricket::Session* session, + const std::string& name) { + LOG_F(LS_WARNING) << "(" << name << ")"; + ASSERT(session == session_); + if (cricket::TransportChannel* channel = session->GetChannel(name)) { + session->DestroyChannel(channel); + } +} + +// HttpClient Signals + +void FileShareSession::OnHttpClientComplete(talk_base::HttpClient* http, + int err) { + LOG_F(LS_INFO) << "(" << err << ", " << http->response().scode << ")"; + ASSERT(http == http_client_); + ASSERT(NULL != session_); + + transfer_name_.clear(); + counter_ = NULL; // counter_ is deleted by HttpClient + http->response().document.reset(); + bool success = (err == 0) && (http->response().scode == talk_base::HC_OK); + + const FileShareManifest::Item& item = manifest_->item(item_transferring_); + talk_base::Pathname local_name; + local_name.SetFilename(item.name); + local_name.SetFolder(local_folder_); + + if (local_name.pathname() != transfer_path_) { + const bool is_folder = (item.type == FileShareManifest::T_FOLDER); + if (success && !talk_base::CreateUniqueFile(local_name, false)) { + LOG(LS_ERROR) << "Couldn't rename downloaded file: " + << local_name.pathname(); + success = false; + } + + talk_base::Pathname temp_name(transfer_path_); + if (is_folder) { + // The folder we want is a subdirectory of the transfer_path_. + temp_name.AppendFolder(item.name); + } + + if (!talk_base::Filesystem::MoveFile(temp_name.pathname(), local_name.pathname())) { + success = false; + LOG(LS_ERROR) << "Couldn't move downloaded file from '" + << temp_name.pathname() << "' to '" + << local_name.pathname(); + } + + if (success && is_folder) { + talk_base::Filesystem::DeleteFile(transfer_path_); + } + } + + if (!success) { + if (!talk_base::Filesystem::DeleteFile(transfer_path_)) { + LOG(LS_ERROR) << "Couldn't delete downloaded file: " << transfer_path_; + } + if (!IsComplete()) { + SetState(FS_FAILURE, false); + } + return; + } + + // We may have skipped over some items (if they are directories, or otherwise + // failed. resize ensures that we populate the skipped entries with empty + // strings. + stored_location_.resize(item_transferring_ + 1); + stored_location_[item_transferring_] = local_name.pathname(); + + // bytes_transferred_ represents the size of items which have completely + // transferred, and is added to the progress of the currently transferring + // items. + if (item.size == FileShareManifest::SIZE_UNKNOWN) { + bytes_transferred_ += 1; + } else { + bytes_transferred_ += item.size; + } + item_transferring_ += 1; + NextDownload(); +} + +void FileShareSession::OnHttpClientClosed(talk_base::HttpClient* http, + int err) { + LOG_F(LS_INFO) << "(" << err << ")"; +} + +// HttpServer Signals + +void FileShareSession::OnHttpRequest(talk_base::HttpServer* server, + talk_base::HttpTransaction* transaction) { + LOG_F(LS_INFO) << "(" << transaction->request()->path << ")"; + ASSERT(server == http_server_); + + std::string path, query; + size_t query_start = transaction->request()->path.find('?'); + if (query_start != std::string::npos) { + path = transaction->request()->path.substr(0, query_start); + query = transaction->request()->path.substr(query_start + 1); + } else { + path = transaction->request()->path; + } + + talk_base::Pathname remote_name(path); + bool preview = (preview_path_ == remote_name.folder()); + bool original = (source_path_ == remote_name.folder()); + + std::string requested_file(remote_name.filename()); + talk_base::transform(requested_file, requested_file.size(), requested_file, + talk_base::url_decode); + + size_t item_index; + const FileShareManifest::Item* item = NULL; + if (preview || original) { + for (size_t i=0; i<manifest_->size(); ++i) { + LOG(LS_INFO) << "++++ " << manifest_->item(i).name + " " << requested_file; + if (manifest_->item(i).name == requested_file) { + item_index = i; + item = &manifest_->item(item_index); + break; + } + } + } + + talk_base::StreamInterface* stream = NULL; + std::string mime_type(MIME_OCTET_STREAM); + + if (!item) { + // Fall through + } else if (preview) { + // Only image previews allowed + unsigned int width = 0, height = 0; + if ((item->type == FileShareManifest::T_IMAGE) + && !query.empty() + && (sscanf(query.c_str(), "width=%u&height=%u", + &width, &height) == 2)) { + width = talk_base::_max<unsigned int>(1, talk_base::_min(width, kMaxPreviewSize)); + height = talk_base::_max<unsigned int>(1, talk_base::_min(height, kMaxPreviewSize)); + std::string pathname; + if (is_sender_) { + talk_base::Pathname local_path; + local_path.SetFolder(local_folder_); + local_path.SetFilename(item->name); + pathname = local_path.pathname(); + } else if ((item_index < stored_location_.size()) + && !stored_location_[item_index].empty()) { + pathname = stored_location_[item_index]; + } + if (!pathname.empty()) { + transactions_.push_back(transaction); + SignalResampleImage(pathname, width, height, transaction); + } + } + } else if (item->type == FileShareManifest::T_FOLDER) { + talk_base::Pathname local_path; + local_path.SetFolder(local_folder_); + local_path.AppendFolder(item->name); + talk_base::TarStream* tar = new talk_base::TarStream; + VERIFY(tar->AddFilter(local_path.folder_name())); + if (tar->Open(local_path.parent_folder(), true)) { + stream = tar; + tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry); + mime_type = "application/x-tar"; + } else { + delete tar; + } + } else if ((item->type == FileShareManifest::T_FILE) + || (item->type == FileShareManifest::T_IMAGE)) { + talk_base::Pathname local_path; + local_path.SetFolder(local_folder_); + local_path.SetFilename(item->name); + talk_base::FileStream* file = new talk_base::FileStream; + LOG(LS_INFO) << "opening file " << local_path.pathname(); + if (file->Open(local_path.pathname().c_str(), "rb")) { + LOG(LS_INFO) << "File opened"; + stream = file; + } else { + delete file; + } + } + + if (!stream) { + transaction->response()->set_error(talk_base::HC_NOT_FOUND); + } else if (original) { + // We should never have more than one original request pending at a time + ASSERT(NULL == counter_); + StreamCounter* counter = new StreamCounter(stream); + counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes); + transaction->response()->set_success(mime_type.c_str(), counter); + transfer_connection_id_ = transaction->connection_id(); + item_transferring_ = item_index; + counter_ = counter; + } else { + // Note: in the preview case, we don't set counter_, so the transferred + // bytes won't be shown as progress, and won't trigger a state change. + transaction->response()->set_success(mime_type.c_str(), stream); + } + + LOG_F(LS_INFO) << "Result: " << transaction->response()->scode; + http_server_->Respond(transaction); +} + +void FileShareSession::OnHttpRequestComplete(talk_base::HttpServer* server, + talk_base::HttpTransaction* transaction, int err) { + LOG_F(LS_INFO) << "(" << transaction->request()->path << ", " << err << ")"; + ASSERT(server == http_server_); + + // We only care about transferred originals + if (transfer_connection_id_ != transaction->connection_id()) + return; + + ASSERT(item_transferring_ < manifest_->size()); + ASSERT(NULL != counter_); + + transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID; + transfer_name_.clear(); + counter_ = NULL; + + if (err == 0) { + const FileShareManifest::Item& item = manifest_->item(item_transferring_); + if (item.size == FileShareManifest::SIZE_UNKNOWN) { + bytes_transferred_ += 1; + } else { + bytes_transferred_ += item.size; + } + } +} + +void FileShareSession::OnHttpConnectionClosed(talk_base::HttpServer* server, + int err, talk_base::StreamInterface* stream) { + LOG_F(LS_INFO) << "(" << err << ")"; + talk_base::Thread::Current()->Dispose(stream); +} + +// TarStream Signals + +void FileShareSession::OnNextEntry(const std::string& name, size_t size) { + LOG_F(LS_VERBOSE) << "(" << name << ", " << size << ")"; + transfer_name_ = name; + SignalNextFile(this); +} + +// Socket Signals + +void FileShareSession::OnProxyAccept(talk_base::AsyncSocket* socket) { + bool is_remote; + if (socket == remote_listener_) { + is_remote = true; + ASSERT(NULL != session_); + } else if (socket == local_listener_) { + is_remote = false; + } else { + ASSERT(false); + return; + } + + while (talk_base::AsyncSocket* accepted = + static_cast<talk_base::AsyncSocket*>(socket->Accept(NULL))) { + + // Check if connection is from localhost. + if (accepted->GetRemoteAddress().ip() != 0x7F000001) { + delete accepted; + continue; + } + + LOG_F(LS_VERBOSE) << (is_remote ? "[remote]" : "[local]"); + + if (is_remote) { + char channel_name[64]; + talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name), + "proxy-%u", next_channel_id_++); + talk_base::StreamInterface* remote = + (NULL != session_) ? CreateChannel(channel_name) : NULL; + if (!remote) { + LOG_F(LS_WARNING) << "CreateChannel(" << channel_name << ") failed"; + delete accepted; + continue; + } + + talk_base::StreamInterface* local = new talk_base::SocketStream(accepted); + StreamRelay* proxy = new StreamRelay(local, remote, 64 * 1024); + proxy->SignalClosed.connect(this, &FileShareSession::OnProxyClosed); + proxies_.push_back(proxy); + proxy->Circulate(); + talk_base::Thread::Current()->Clear(this, MSG_PROXY_WAIT); + } else { + talk_base::StreamInterface* local = new talk_base::SocketStream(accepted); + http_server_->HandleConnection(local); + } + } +} + +void FileShareSession::OnProxyClosed(StreamRelay* proxy, int error) { + ProxyList::iterator it = std::find(proxies_.begin(), proxies_.end(), proxy); + if (it == proxies_.end()) { + ASSERT(false); + return; + } + + LOG_F(LS_VERBOSE) << "(" << error << ")"; + + proxies_.erase(it); + talk_base::Thread::Current()->Dispose(proxy); + + if (proxies_.empty() && IsComplete() && !IsClosed()) { + talk_base::Thread::Current()->PostDelayed(kProxyWait, this, MSG_PROXY_WAIT); + } +} + + +void FileShareSession::OnUpdateBytes(size_t count) { + SignalUpdateProgress(this); +} + +// Internal Helpers + +void FileShareSession::GenerateTemporaryPrefix(std::string* prefix) { + std::string data = cricket::CreateRandomString(32); + ASSERT(NULL != prefix); + prefix->assign("/temporary/"); + prefix->append(talk_base::MD5(data)); + prefix->append("/"); +} + +void FileShareSession::GetItemNetworkPath(size_t index, bool preview, + std::string* path) { + ASSERT(index < manifest_->size()); + ASSERT(NULL != path); + + // preview_path_ and source_path_ are url path segments, which are composed + // with the address of the localhost p2p proxy to provide a url which IE can + // use. + + std::string ue_name; + const std::string& name = manifest_->item(index).name; + talk_base::transform(ue_name, name.length() * 3, name, talk_base::url_encode); + + talk_base::Pathname pathname; + pathname.SetFolder(preview ? preview_path_ : source_path_); + pathname.SetFilename(ue_name); + *path = pathname.pathname(); +} + +bool FileShareSession::GetItemBaseUrl(size_t index, bool preview, + std::string* url) { + // This function composes a URL to the referenced item. It may be a local + // file url (file:///...), or a remote peer url relayed through localhost + // (http://...) + + ASSERT(NULL != url); + if (index >= manifest_->size()) { + ASSERT(false); + return false; + } + + const FileShareManifest::Item& item = manifest_->item(index); + + bool is_remote; + if (is_sender_) { + if (!preview) { + talk_base::Pathname path(local_folder_); + path.SetFilename(item.name); + *url = path.url(); + return true; + } + is_remote = false; + } else { + if ((index < stored_location_.size()) && !stored_location_[index].empty()) { + if (!preview) { + *url = talk_base::Pathname(stored_location_[index]).url(); + return true; + } + // Note: Using the local downloaded files as a source for previews is + // desireable, because it means that previews can be regenerated if IE's + // cached versions get flushed for some reason, and the remote side is + // not available. However, it has the downside that IE _must_ regenerate + // the preview locally, which takes time, memory and CPU. Eventually, + // we will unify the remote and local cached copy through some sort of + // smart http proxying. In the meantime, always use the remote url, to + // eliminate the annoying transition from remote to local caching. + //is_remote = false; + is_remote = true; + } else { + is_remote = true; + } + } + + talk_base::SocketAddress address; + if (!GetProxyAddress(address, is_remote)) + return false; + + std::string path; + GetItemNetworkPath(index, preview, &path); + talk_base::Url<char> make_url(path.c_str(), + address.IPAsString().c_str(), + address.port()); + *url = make_url.url(); + return true; +} + +bool FileShareSession::GetProxyAddress(talk_base::SocketAddress& address, + bool is_remote) { + talk_base::AsyncSocket*& proxy_listener = + is_remote ? remote_listener_ : local_listener_; + + if (!proxy_listener) { + talk_base::AsyncSocket* listener = + talk_base::Thread::Current()->socketserver() + ->CreateAsyncSocket(SOCK_STREAM); + if (!listener) + return false; + + talk_base::SocketAddress bind_address("127.0.0.1", 0); + + if ((listener->Bind(bind_address) != 0) + || (listener->Listen(5) != 0)) { + delete listener; + return false; + } + + LOG(LS_INFO) << "Proxy listener available @ " + << listener->GetLocalAddress().ToString(); + + listener->SignalReadEvent.connect(this, &FileShareSession::OnProxyAccept); + proxy_listener = listener; + } + + if (proxy_listener->GetState() == talk_base::Socket::CS_CLOSED) { + if (is_remote) { + address = remote_listener_address_; + return true; + } + return false; + } + + address = proxy_listener->GetLocalAddress(); + return !address.IsAny(); +} + +talk_base::StreamInterface* FileShareSession::CreateChannel( + const std::string& channel_name) { + ASSERT(NULL != session_); + + // Send a heads-up for our new channel + cricket::Session::XmlElements els; + buzz::XmlElement* xel_channel = new buzz::XmlElement(QN_SHARE_CHANNEL, true); + xel_channel->AddAttr(buzz::QN_NAME, channel_name); + els.push_back(xel_channel); + session_->SendInfoMessage(els); + + cricket::PseudoTcpChannel* channel = + new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_); + VERIFY(channel->Connect(channel_name)); + return channel->GetStream(); +} + +void FileShareSession::SetState(FileShareState state, bool prevent_close) { + if (state == state_) + return; + + if (IsComplete()) { + // Entering a completion state is permanent. + ASSERT(false); + return; + } + + state_ = state; + if (IsComplete()) { + // All completion states auto-close except for FS_COMPLETE + bool close = (state_ > FS_COMPLETE) || !prevent_close; + if (close) { + DoClose(true); + } + } + + SignalState(state_); +} + +void FileShareSession::OnInitiate() { + // Cache the variables we will need, in case session_ goes away + is_sender_ = session_->initiator(); + jid_ = buzz::Jid(session_->remote_name()); + manifest_ = new FileShareManifest(description()->manifest); + source_path_ = description()->source_path; + preview_path_ = description()->preview_path; + + if (local_folder_.empty()) { + LOG(LS_ERROR) << "FileShareSession - no local folder, using temp"; + talk_base::Pathname temp_folder; + talk_base::Filesystem::GetTemporaryFolder(temp_folder, true, NULL); + local_folder_ = temp_folder.pathname(); + } + LOG(LS_INFO) << session_->state(); + SetState(FS_OFFER, false); +} + +void FileShareSession::NextDownload() { + if (FS_TRANSFER != state_) + return; + + if (item_transferring_ >= manifest_->size()) { + // Notify the other side that transfer has completed + cricket::Session::XmlElements els; + els.push_back(new buzz::XmlElement(QN_SHARE_COMPLETE, true)); + session_->SendInfoMessage(els); + SetState(FS_COMPLETE, !proxies_.empty()); + return; + } + + const FileShareManifest::Item& item = manifest_->item(item_transferring_); + if ((item.type != FileShareManifest::T_FILE) + && (item.type != FileShareManifest::T_IMAGE) + && (item.type != FileShareManifest::T_FOLDER)) { + item_transferring_ += 1; + NextDownload(); + return; + } + + const bool is_folder = (item.type == FileShareManifest::T_FOLDER); + talk_base::Pathname temp_name; + temp_name.SetFilename(item.name); + if (!talk_base::CreateUniqueFile(temp_name, !is_folder)) { + SetState(FS_FAILURE, false); + return; + } + + talk_base::StreamInterface* stream = NULL; + if (is_folder) { + // Convert unique filename into unique foldername + temp_name.AppendFolder(temp_name.filename()); + temp_name.SetFilename(""); + talk_base::TarStream* tar = new talk_base::TarStream; + // Note: the 'target' directory will be a subdirectory of the transfer_path_ + talk_base::Pathname target; + target.SetFolder(item.name); + tar->AddFilter(target.pathname()); + if (!tar->Open(temp_name.pathname(), false)) { + delete tar; + SetState(FS_FAILURE, false); + return; + } + stream = tar; + tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry); + } else { + talk_base::FileStream* file = new talk_base::FileStream; + if (!file->Open(temp_name.pathname().c_str(), "wb")) { + delete file; + talk_base::Filesystem::DeleteFile(temp_name); + SetState(FS_FAILURE, false); + return; + } + stream = file; + } + + ASSERT(NULL != stream); + transfer_path_ = temp_name.pathname(); + + std::string remote_path; + GetItemNetworkPath(item_transferring_, false, &remote_path); + + StreamCounter* counter = new StreamCounter(stream); + counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes); + counter_ = counter; + + http_client_->reset(); + http_client_->set_server(talk_base::SocketAddress(jid_.Str(), 0, false)); + http_client_->request().verb = talk_base::HV_GET; + http_client_->request().path = remote_path; + http_client_->response().document.reset(counter); + http_client_->start(); +} + + +const FileShareSession::FileShareDescription* FileShareSession::description() +const { + ASSERT(NULL != session_); + const cricket::SessionDescription* desc = + session_->initiator() ? session_->description() + : session_->remote_description(); + return static_cast<const FileShareDescription*>(desc); +} + +void FileShareSession::DoClose(bool terminate) { + ASSERT(!is_closed_); + ASSERT(IsComplete()); + ASSERT(NULL != session_); + + is_closed_ = true; + + if (http_client_) { + http_client_->reset(); + } + if (http_server_) { + http_server_->CloseAll(true); + // Currently, CloseAll doesn't result in OnHttpRequestComplete callback. + // If we change that, the following resetting won't be necessary. + transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID; + transfer_name_.clear(); + counter_ = NULL; + } + // 'reset' and 'CloseAll' cause counter_ to clear. + ASSERT(NULL == counter_); + + if (remote_listener_) { + // Cache the address for the remote_listener_, so that we can continue to + // present a consistent URL for remote previews, which is necessary for IE + // to continue using its cached copy. + remote_listener_address_ = remote_listener_->GetLocalAddress(); + remote_listener_->Close(); + LOG(LS_INFO) << "Proxy listener closed @ " + << remote_listener_address_.ToString(); + } + + if (terminate) { + session_->Terminate(); + } +} + +////////////////////////////// +/// FileShareSessionClient // +//////////////////////////// + +void FileShareSessionClient::OnSessionCreate(cricket::Session* session, + bool received_initiate) { + VERIFY(sessions_.insert(session).second); + if (received_initiate) { + FileShareSession* share = new FileShareSession(session, user_agent_); + SignalFileShareSessionCreate(share); + UNUSED(share); // FileShareSession registers itself with the UI + } +} + +void FileShareSessionClient::OnSessionDestroy(cricket::Session* session) { + VERIFY(1 == sessions_.erase(session)); +} + +const cricket::SessionDescription* FileShareSessionClient::CreateSessionDescription( + const buzz::XmlElement* element) { + FileShareSession::FileShareDescription* share_desc = + new FileShareSession::FileShareDescription; + + if (element->Name() != QN_SHARE_DESCRIPTION) + return share_desc; + + const buzz::XmlElement* manifest = element->FirstNamed(QN_SHARE_MANIFEST); + const buzz::XmlElement* protocol = element->FirstNamed(QN_SHARE_PROTOCOL); + + if (!manifest || !protocol) + return share_desc; + + for (const buzz::XmlElement* item = manifest->FirstElement(); + item != NULL; item = item->NextElement()) { + bool is_folder; + if (item->Name() == QN_SHARE_FOLDER) { + is_folder = true; + } else if (item->Name() == QN_SHARE_FILE) { + is_folder = false; + } else { + continue; + } + std::string name; + if (const buzz::XmlElement* el_name = item->FirstNamed(QN_SHARE_NAME)) { + name = el_name->BodyText(); + } + if (name.empty()) { + continue; + } + size_t size = FileShareManifest::SIZE_UNKNOWN; + if (item->HasAttr(QN_SIZE)) { + size = strtoul(item->Attr(QN_SIZE).c_str(), NULL, 10); + } + if (is_folder) { + share_desc->manifest.AddFolder(name, size); + } else { + // Check if there is a valid image description for this file. + if (const buzz::XmlElement* image = item->FirstNamed(QN_SHARE_IMAGE)) { + if (image->HasAttr(QN_WIDTH) && image->HasAttr(QN_HEIGHT)) { + size_t width = strtoul(image->Attr(QN_WIDTH).c_str(), NULL, 10); + size_t height = strtoul(image->Attr(QN_HEIGHT).c_str(), NULL, 10); + if (AllowedImageDimensions(width, height)) { + share_desc->manifest.AddImage(name, size, width, height); + continue; + } + } + } + share_desc->manifest.AddFile(name, size); + } + } + + if (const buzz::XmlElement* http = protocol->FirstNamed(QN_SHARE_HTTP)) { + share_desc->supports_http = true; + for (const buzz::XmlElement* url = http->FirstNamed(QN_SHARE_URL); + url != NULL; url = url->NextNamed(QN_SHARE_URL)) { + if (url->Attr(buzz::QN_NAME) == kHttpSourcePath) { + share_desc->source_path = url->BodyText(); + } else if (url->Attr(buzz::QN_NAME) == kHttpPreviewPath) { + share_desc->preview_path = url->BodyText(); + } + } + } + + return share_desc; +} + +buzz::XmlElement* FileShareSessionClient::TranslateSessionDescription( + const cricket::SessionDescription* description) { + + const FileShareSession::FileShareDescription* share_desc = + static_cast<const FileShareSession::FileShareDescription*>(description); + + scoped_ptr<buzz::XmlElement> el(new buzz::XmlElement(QN_SHARE_DESCRIPTION, + true)); + + const FileShareManifest& manifest = share_desc->manifest; + el->AddElement(new buzz::XmlElement(QN_SHARE_MANIFEST)); + for (size_t i=0; i<manifest.size(); ++i) { + const FileShareManifest::Item& item = manifest.item(i); + buzz::QName qname; + if (item.type == FileShareManifest::T_FOLDER) { + qname = QN_SHARE_FOLDER; + } else if ((item.type == FileShareManifest::T_FILE) + || (item.type == FileShareManifest::T_IMAGE)) { + qname = QN_SHARE_FILE; + } else { + ASSERT(false); + continue; + } + el->AddElement(new buzz::XmlElement(qname), 1); + if (item.size != FileShareManifest::SIZE_UNKNOWN) { + char buffer[256]; + talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.size); + el->AddAttr(QN_SIZE, buffer, 2); + } + buzz::XmlElement* el_name = new buzz::XmlElement(QN_SHARE_NAME); + el_name->SetBodyText(item.name); + el->AddElement(el_name, 2); + if ((item.type == FileShareManifest::T_IMAGE) + && AllowedImageDimensions(item.width, item.height)) { + el->AddElement(new buzz::XmlElement(QN_SHARE_IMAGE), 2); + char buffer[256]; + talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.width); + el->AddAttr(QN_WIDTH, buffer, 3); + talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.height); + el->AddAttr(QN_HEIGHT, buffer, 3); + } + } + + el->AddElement(new buzz::XmlElement(QN_SHARE_PROTOCOL)); + if (share_desc->supports_http) { + el->AddElement(new buzz::XmlElement(QN_SHARE_HTTP), 1); + if (!share_desc->source_path.empty()) { + buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL); + url->SetAttr(buzz::QN_NAME, kHttpSourcePath); + url->SetBodyText(share_desc->source_path); + el->AddElement(url, 2); + } + if (!share_desc->preview_path.empty()) { + buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL); + url->SetAttr(buzz::QN_NAME, kHttpPreviewPath); + url->SetBodyText(share_desc->preview_path); + el->AddElement(url, 2); + } + } + + return el.release(); +} + +FileShareSession *FileShareSessionClient::CreateFileShareSession() { + cricket::Session* session = sm_->CreateSession(jid_.Str(), + NS_GOOGLE_SHARE); + FileShareSession* share = new FileShareSession(session, user_agent_); + SignalFileShareSessionCreate(share); + return share; +} + + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h new file mode 100644 index 0000000..b0802b4 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h @@ -0,0 +1,250 @@ +#ifndef TALK_APP_WIN32_FILESHARE_H__ +#define TALK_APP_WIN32_FILESHARE_H__ +#include "talk/base/messagequeue.h" +#include "talk/base/socketpool.h" +#include "talk/base/stringutils.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/xmpp/jid.h" + +class StreamCounter; +class StreamRelay; + +namespace talk_base { + class HttpClient; + class HttpServer; + class HttpTransaction; +} + +extern const std::string NS_GOOGLE_SHARE; + + +namespace cricket { + +/////////////////////////////////////////////////////////////////////////////// +// FileShareManifest +/////////////////////////////////////////////////////////////////////////////// + +class FileShareManifest { +public: + enum Type { T_FILE, T_IMAGE, T_FOLDER }; + enum { SIZE_UNKNOWN = talk_base::SIZE_UNKNOWN }; + + struct Item { + Type type; + std::string name; + size_t size, width, height; + }; + typedef std::vector<Item> ItemList; + + inline bool empty() const { return items_.empty(); } + inline size_t size() const { return items_.size(); } + inline const Item& item(size_t index) const { return items_[index]; } + + void AddFile(const std::string& name, size_t size); + void AddImage(const std::string& name, size_t size, + size_t width, size_t height); + void AddFolder(const std::string& name, size_t size); + + size_t GetItemCount(Type t) const; + inline size_t GetFileCount() const { return GetItemCount(T_FILE); } + inline size_t GetImageCount() const { return GetItemCount(T_IMAGE); } + inline size_t GetFolderCount() const { return GetItemCount(T_FOLDER); } + +private: + ItemList items_; +}; + + +enum FileShareState { + FS_NONE, // Initialization + FS_OFFER, // Offer extended + FS_TRANSFER, // In progress + FS_COMPLETE, // Completed successfully + FS_LOCAL_CANCEL, // Local side cancelled + FS_REMOTE_CANCEL, // Remote side cancelled + FS_FAILURE // An error occurred during transfer +}; + + +class FileShareSession + : public talk_base::StreamPool, + public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + struct FileShareDescription : public cricket::SessionDescription { + FileShareManifest manifest; + bool supports_http; + std::string source_path; + std::string preview_path; + FileShareDescription() : supports_http(false) { } + }; + + FileShareSession(cricket::Session* session, const std::string &user_agent); + virtual ~FileShareSession(); + + bool IsComplete() const; + bool IsClosed() const; + FileShareState state() const; + sigslot::signal1<FileShareState> SignalState; + sigslot::signal1<FileShareSession*> SignalNextFile; + sigslot::signal1<FileShareSession*> SignalUpdateProgress; + sigslot::signal4<std::string, int, int, talk_base::HttpTransaction*> SignalResampleImage; + + void ResampleComplete(talk_base::StreamInterface *si, talk_base::HttpTransaction *trans, bool success); + + bool is_sender() const; + const buzz::Jid& jid() const; + const FileShareManifest* manifest() const; + const std::string& local_folder() const; + + void SetLocalFolder(const std::string& folder) { local_folder_ = folder; } + void Share(const buzz::Jid& jid, FileShareManifest* manifest); + + void Accept(); + void Decline(); + void Cancel(); + + bool GetItemUrl(size_t index, std::string* url); + bool GetImagePreviewUrl(size_t index, size_t width, size_t height, + std::string* url); + // Returns true if the transferring item size is known + bool GetProgress(size_t& bytes) const; + // Returns true if the total size is known + bool GetTotalSize(size_t& bytes) const; + // Returns true if currently transferring item name is known + bool GetCurrentItemName(std::string* name); + + // TODO: Eliminate this eventually? + cricket::Session* session() { return session_; } + + // StreamPool Interface + virtual talk_base::StreamInterface* + RequestConnectedStream(const talk_base::SocketAddress& remote, int* err); + virtual void ReturnConnectedStream(talk_base::StreamInterface* stream); + + // MessageHandler Interface + virtual void OnMessage(talk_base::Message* msg); + + void GetItemNetworkPath(size_t index, bool preview, std::string* path); + +private: + typedef std::list<StreamRelay*> ProxyList; + typedef std::list<talk_base::HttpTransaction*> TransactionList; + + // Session Signals + void OnSessionState(cricket::Session* session, cricket::Session::State state); + void OnSessionInfoMessage(cricket::Session* session, + const cricket::Session::XmlElements& els); + void OnSessionChannelGone(cricket::Session* session, + const std::string& name); + + // HttpClient Signals + void OnHttpClientComplete(talk_base::HttpClient* http, int err); + void OnHttpClientClosed(talk_base::HttpClient* http, int err); + + // HttpServer Signals + void OnHttpRequest(talk_base::HttpServer* server, + talk_base::HttpTransaction* transaction); + void OnHttpRequestComplete(talk_base::HttpServer* server, + talk_base::HttpTransaction* transaction, + int err); + void OnHttpConnectionClosed(talk_base::HttpServer* server, + int err, + talk_base::StreamInterface* stream); + + // TarStream Signals + void OnNextEntry(const std::string& name, size_t size); + + // Socket Signals + void OnProxyAccept(talk_base::AsyncSocket* socket); + void OnProxyClosed(StreamRelay* proxy, int error); + + // StreamCounterSignals + void OnUpdateBytes(size_t count); + + // Internal Helpers + void GenerateTemporaryPrefix(std::string* prefix); + bool GetItemBaseUrl(size_t index, bool preview, std::string* url); + bool GetProxyAddress(talk_base::SocketAddress& address, bool is_remote); + talk_base::StreamInterface* CreateChannel(const std::string& channel_name); + void SetState(FileShareState state, bool prevent_close); + void OnInitiate(); + void NextDownload(); + const FileShareDescription* description() const; + void DoClose(bool terminate); + + cricket::Session* session_; + FileShareState state_; + bool is_closed_; + bool is_sender_; + buzz::Jid jid_; + FileShareManifest* manifest_; + std::string source_path_; + std::string preview_path_; + std::string local_folder_; + + // The currently active p2p streams to our peer + talk_base::StreamCache pool_; + // The http client state (client only) + talk_base::HttpClient* http_client_; + // The http server state (server only) + talk_base::HttpServer* http_server_; + // The connection id of the currently transferring file (server) + int transfer_connection_id_; + // The counter for the currently transferring file + const StreamCounter* counter_; + // The number of manifest items that have successfully transferred + size_t item_transferring_; + // The byte count of successfully transferred items + size_t bytes_transferred_; + // Where the currently transferring item is being (temporarily) saved (client) + std::string transfer_path_; + // The name of the currently transferring item + std::string transfer_name_; + // Where the files are saved after transfer (client) + std::vector<std::string> stored_location_; + // Was it a local cancel? Or a remote cancel? + bool local_cancel_; + // Proxy socket for local IE http requests + talk_base::AsyncSocket* local_listener_; + // Proxy socket for remote IE http requests + talk_base::AsyncSocket* remote_listener_; + // Cached address of remote_listener_ + talk_base::SocketAddress remote_listener_address_; + // Uniqueness for channel names + size_t next_channel_id_; + // Proxy relays + ProxyList proxies_; + std::string user_agent_; + TransactionList transactions_; +}; + +class FileShareSessionClient : public SessionClient +{ + public: + FileShareSessionClient(SessionManager *sm, buzz::Jid jid, const std::string &user_agent) : sm_(sm), jid_(jid), + user_agent_(user_agent) {} + virtual void OnSessionCreate(cricket::Session* session, + bool received_initiate); + virtual void OnSessionDestroy(cricket::Session* session); + virtual const cricket::SessionDescription* CreateSessionDescription(const buzz::XmlElement* element); + virtual buzz::XmlElement* TranslateSessionDescription(const cricket::SessionDescription* description); + FileShareSession *CreateFileShareSession(); + + sigslot::signal1<FileShareSession*> SignalFileShareSessionCreate; + sigslot::signal1<FileShareSession*> SignalFileShareSessionDestroy; + + private: + SessionManager *sm_; + buzz::Jid jid_; + friend class FileShareSession; + typedef std::set<cricket::Session*> SessionSet; + SessionSet sessions_; + std::string user_agent_; +}; + +} // namespace cricket + +#endif // TALK_APP_WIN32_FILESHARE_H__ diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.am b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am new file mode 100644 index 0000000..5d489b6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am @@ -0,0 +1,27 @@ +EXTRA_DIST=gipslitemediaengine.cc gipslitemediaengine.h +if GIPS +nodist_libcricketsessionphone_la_SOURCES=gipsmediaengine.cc \ + gipsstatsmonitor.cc +else +dist_libcricketsessionphone_la_SOURCES=linphonemediaengine.cc +endif + +libcricketsessionphone_la_SOURCES = audiomonitor.cc \ + channelmanager.cc \ + voicechannel.cc \ + call.cc \ + phonesessionclient.cc + +noinst_HEADERS = audiomonitor.h \ + channelmanager.h \ + linphonemediaengine.h \ + mediaengine.h \ + phonesessionclient.h \ + voicechannel.h \ + call.h \ + mediachannel.h \ + codec.h + +AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS) +noinst_LTLIBRARIES = libcricketsessionphone.la + diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.in b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in new file mode 100644 index 0000000..8dfdbc8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in @@ -0,0 +1,485 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = talk/session/phone +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcricketsessionphone_la_LIBADD = +am_libcricketsessionphone_la_OBJECTS = audiomonitor.lo \ + channelmanager.lo voicechannel.lo call.lo \ + phonesessionclient.lo +am__dist_libcricketsessionphone_la_SOURCES_DIST = \ + linphonemediaengine.cc +@GIPS_FALSE@dist_libcricketsessionphone_la_OBJECTS = \ +@GIPS_FALSE@ linphonemediaengine.lo +@GIPS_TRUE@nodist_libcricketsessionphone_la_OBJECTS = \ +@GIPS_TRUE@ gipsmediaengine.lo gipsstatsmonitor.lo +libcricketsessionphone_la_OBJECTS = \ + $(am_libcricketsessionphone_la_OBJECTS) \ + $(dist_libcricketsessionphone_la_OBJECTS) \ + $(nodist_libcricketsessionphone_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libcricketsessionphone_la_SOURCES) \ + $(dist_libcricketsessionphone_la_SOURCES) \ + $(nodist_libcricketsessionphone_la_SOURCES) +DIST_SOURCES = $(libcricketsessionphone_la_SOURCES) \ + $(am__dist_libcricketsessionphone_la_SOURCES_DIST) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +EXTRA_DIST = gipslitemediaengine.cc gipslitemediaengine.h +@GIPS_TRUE@nodist_libcricketsessionphone_la_SOURCES = gipsmediaengine.cc \ +@GIPS_TRUE@ gipsstatsmonitor.cc + +@GIPS_FALSE@dist_libcricketsessionphone_la_SOURCES = linphonemediaengine.cc +libcricketsessionphone_la_SOURCES = audiomonitor.cc \ + channelmanager.cc \ + voicechannel.cc \ + call.cc \ + phonesessionclient.cc + +noinst_HEADERS = audiomonitor.h \ + channelmanager.h \ + linphonemediaengine.h \ + mediaengine.h \ + phonesessionclient.h \ + voicechannel.h \ + call.h \ + mediachannel.h \ + codec.h + +AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS) +noinst_LTLIBRARIES = libcricketsessionphone.la +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/phone/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/session/phone/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcricketsessionphone.la: $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_DEPENDENCIES) + $(CXXLINK) $(libcricketsessionphone_la_LDFLAGS) $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audiomonitor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channelmanager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsmediaengine.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsstatsmonitor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linphonemediaengine.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/phonesessionclient.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/voicechannel.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc new file mode 100644 index 0000000..c1f3614 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc @@ -0,0 +1,120 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/voicechannel.h" +#include <cassert> + +namespace cricket { + +const uint32 MSG_MONITOR_POLL = 1; +const uint32 MSG_MONITOR_START = 2; +const uint32 MSG_MONITOR_STOP = 3; +const uint32 MSG_MONITOR_SIGNAL = 4; + +AudioMonitor::AudioMonitor(VoiceChannel *voice_channel, + talk_base::Thread *monitor_thread) { + voice_channel_ = voice_channel; + monitoring_thread_ = monitor_thread; + monitoring_ = false; +} + +AudioMonitor::~AudioMonitor() { + voice_channel_->worker_thread()->Clear(this); + monitoring_thread_->Clear(this); +} + +void AudioMonitor::Start(int milliseconds) { + rate_ = milliseconds; + if (rate_ < 100) + rate_ = 100; + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START); +} + +void AudioMonitor::Stop() { + voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP); +} + +void AudioMonitor::OnMessage(talk_base::Message *message) { + talk_base::CritScope cs(&crit_); + + switch (message->message_id) { + case MSG_MONITOR_START: + assert(talk_base::Thread::Current() == voice_channel_->worker_thread()); + if (!monitoring_) { + monitoring_ = true; + PollVoiceChannel(); + } + break; + + case MSG_MONITOR_STOP: + assert(talk_base::Thread::Current() == voice_channel_->worker_thread()); + if (monitoring_) { + monitoring_ = false; + voice_channel_->worker_thread()->Clear(this); + } + break; + + case MSG_MONITOR_POLL: + assert(talk_base::Thread::Current() == voice_channel_->worker_thread()); + PollVoiceChannel(); + break; + + case MSG_MONITOR_SIGNAL: + { + assert(talk_base::Thread::Current() == monitoring_thread_); + AudioInfo info = audio_info_; + crit_.Leave(); + SignalUpdate(this, audio_info_); + crit_.Enter(); + } + break; + } +} + +void AudioMonitor::PollVoiceChannel() { + talk_base::CritScope cs(&crit_); + assert(talk_base::Thread::Current() == voice_channel_->worker_thread()); + + // Gather connection infos + audio_info_.input_level = voice_channel_->GetInputLevel_w(); + audio_info_.output_level = voice_channel_->GetOutputLevel_w(); + + // Signal the monitoring thread, start another poll timer + monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL); + voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL); +} + +VoiceChannel *AudioMonitor::voice_channel() { + return voice_channel_; +} + +talk_base::Thread *AudioMonitor::monitor_thread() { + return monitoring_thread_; +} + +} diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h new file mode 100644 index 0000000..2bfd784 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h @@ -0,0 +1,74 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_PHONE_AUDIOMONITOR_H_ +#define _CRICKET_PHONE_AUDIOMONITOR_H_ + +#include "talk/base/thread.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/base/port.h" +#include <vector> + +namespace cricket { + +class VoiceChannel; + + +struct AudioInfo { + int input_level; + int output_level; +}; + +class AudioMonitor : public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + AudioMonitor(VoiceChannel* voice_channel, talk_base::Thread *monitor_thread); + ~AudioMonitor(); + + void Start(int cms); + void Stop(); + + VoiceChannel* voice_channel(); + talk_base::Thread *monitor_thread(); + + sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate; + +protected: + void OnMessage(talk_base::Message *message); + void PollVoiceChannel(); + + AudioInfo audio_info_; + VoiceChannel* voice_channel_; + talk_base::Thread* monitoring_thread_; + talk_base::CriticalSection crit_; + uint32 rate_; + bool monitoring_; +}; + +} + +#endif // _CRICKET_PHONE_AUDIOMONITOR_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.cc b/Plugins/jingle/libjingle/talk/session/phone/call.cc new file mode 100644 index 0000000..1676945 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/call.cc @@ -0,0 +1,336 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#include "talk/session/phone/call.h" + +namespace cricket { + +const uint32 MSG_CHECKAUTODESTROY = 1; +const uint32 MSG_TERMINATECALL = 2; + +namespace { +const int kSendToVoicemailTimeout = 1000*20; +const int kNoVoicemailTimeout = 1000*180; +const int kMediaMonitorInterval = 1000*15; +} + +Call::Call(PhoneSessionClient *session_client) + : muted_(false), send_to_voicemail_(true) +{ + session_client_ = session_client; + id_ = CreateRandomId(); +} + +Call::~Call() { + while (sessions_.begin() != sessions_.end()) { + Session *session = sessions_[0]; + RemoveSession(session); + session_client_->session_manager()->DestroySession(session); + } + talk_base::Thread::Current()->Clear(this); +} + +Session *Call::InitiateSession(const buzz::Jid &jid, + std::vector<buzz::XmlElement*>* extra_xml) { + Session *session = session_client_->CreateSession(this); + AddSession(session); + session->Initiate(jid.Str(), extra_xml, + session_client_->CreateOfferSessionDescription()); + + // After this timeout, terminate the call because the callee isn't + // answering + session_client_->session_manager()->signaling_thread()->Clear(this, + MSG_TERMINATECALL); + session_client_->session_manager()->signaling_thread()->PostDelayed( + send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout, + this, MSG_TERMINATECALL); + return session; +} + +void Call::AcceptSession(Session *session) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) { + session->Accept(session_client_->CreateAcceptSessionDescription( + session->remote_description())); + } +} + +void Call::RedirectSession(Session *session, const buzz::Jid &to) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Redirect(to.Str()); + + session_client_->session_manager()->signaling_thread()->Clear(this, + MSG_TERMINATECALL); + session_client_->session_manager()->signaling_thread()->PostDelayed( + send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout, + this, MSG_TERMINATECALL); +} + +void Call::RejectSession(Session *session) { + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); + if (it != sessions_.end()) + session->Reject(); +} + +void Call::TerminateSession(Session *session) { + assert(std::find(sessions_.begin(), sessions_.end(), session) + != sessions_.end()); + std::vector<Session *>::iterator it; + it = std::find(sessions_.begin(), sessions_.end(), session); + if (it != sessions_.end()) + (*it)->Terminate(); +} + +void Call::Terminate() { + // Copy the list so that we can iterate over it in a stable way + std::vector<Session *> sessions = sessions_; + + // There may be more than one session to terminate + std::vector<Session *>::iterator it; + for (it = sessions.begin(); it != sessions.end(); it++) + TerminateSession(*it); + +} + +void Call::OnMessage(talk_base::Message *message) { + switch (message->message_id) { + case MSG_CHECKAUTODESTROY: + // If no more sessions for this call, delete it + if (sessions_.size() == 0) + session_client_->DestroyCall(this); + break; + case MSG_TERMINATECALL: + // Signal to the user that a timeout has happened and the call should + // be sent to voicemail. + if (send_to_voicemail_) { + SignalSetupToCallVoicemail(); + } + + // Callee didn't answer - terminate call + Terminate(); + break; + } +} + +const std::vector<Session *> &Call::sessions() { + return sessions_; +} + +void Call::AddSession(Session *session) { + // Add session to list, create voice channel for this session + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + session->SignalReceivedTerminateReason + .connect(this, &Call::OnReceivedTerminateReason); + + VoiceChannel *channel + = session_client_->channel_manager()->CreateVoiceChannel(session); + channel_map_[session->id()] = channel; + + // Start the media monitor for this voicechannel + channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor); + channel->StartMediaMonitor(kMediaMonitorInterval); + + // If this call has the focus, enable this channel + if (session_client_->GetFocus() == this) + channel->Enable(true); + + // Signal client + SignalAddSession(this, session); +} + +void Call::RemoveSession(Session *session) { + // Remove session from list + std::vector<Session *>::iterator it_session; + it_session = std::find(sessions_.begin(), sessions_.end(), session); + if (it_session == sessions_.end()) + return; + sessions_.erase(it_session); + + // Destroy session channel + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = it_channel->second; + channel_map_.erase(it_channel); + channel->StopMediaMonitor(); + session_client_->channel_manager()->DestroyVoiceChannel(channel); + } + + // Signal client + SignalRemoveSession(this, session); + + + + // The call auto destroys when the lass session is removed + talk_base::Thread::Current()->Post(this, MSG_CHECKAUTODESTROY); +} + +VoiceChannel* Call::GetChannel(Session* session) { + std::map<SessionID, VoiceChannel *>::iterator it + = channel_map_.find(session->id()); + assert(it != channel_map_.end()); + return it->second; +} + +void Call::EnableChannels(bool enable) { + std::vector<Session *>::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Enable(enable); + } +} + +void Call::Mute(bool mute) { + muted_ = mute; + std::vector<Session *>::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); it++) { + VoiceChannel *channel = channel_map_[(*it)->id()]; + if (channel != NULL) + channel->Mute(mute); + } +} + + +void Call::Join(Call *call, bool enable) { + while (call->sessions_.size() != 0) { + // Move session + Session *session = call->sessions_[0]; + call->sessions_.erase(call->sessions_.begin()); + sessions_.push_back(session); + session->SignalState.connect(this, &Call::OnSessionState); + session->SignalError.connect(this, &Call::OnSessionError); + session->SignalReceivedTerminateReason + .connect(this, &Call::OnReceivedTerminateReason); + + // Move channel + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = call->channel_map_.find(session->id()); + if (it_channel != call->channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + call->channel_map_.erase(it_channel); + channel_map_[session->id()] = channel; + channel->Enable(enable); + } + } +} + +void Call::StartConnectionMonitor(Session *session, int cms) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor); + channel->StartConnectionMonitor(cms); + } +} + +void Call::StopConnectionMonitor(Session *session) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopConnectionMonitor(); + channel->SignalConnectionMonitor.disconnect(this); + } +} + +void Call::StartAudioMonitor(Session *session, int cms) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor); + channel->StartAudioMonitor(cms); + } +} + +void Call::StopAudioMonitor(Session *session) { + std::map<SessionID, VoiceChannel *>::iterator it_channel; + it_channel = channel_map_.find(session->id()); + if (it_channel != channel_map_.end()) { + VoiceChannel *channel = (*it_channel).second; + channel->StopAudioMonitor(); + channel->SignalAudioMonitor.disconnect(this); + } +} + + +void Call::OnConnectionMonitor(VoiceChannel *channel, + const std::vector<ConnectionInfo> &infos) { + SignalConnectionMonitor(this, channel->session(), infos); +} + +void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) { + SignalAudioMonitor(this, channel->session(), info); +} + +void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) { + SignalMediaMonitor(this, channel->session(), info); +} + +uint32 Call::id() { + return id_; +} + +void Call::OnSessionState(Session *session, Session::State state) { + switch (state) { + case Session::STATE_RECEIVEDACCEPT: + case Session::STATE_RECEIVEDREJECT: + case Session::STATE_RECEIVEDTERMINATE: + session_client_->session_manager()->signaling_thread()->Clear(this, + MSG_TERMINATECALL); + break; + } + SignalSessionState(this, session, state); +} + +void Call::OnSessionError(Session *session, Session::Error error) { + session_client_->session_manager()->signaling_thread()->Clear(this, + MSG_TERMINATECALL); + SignalSessionError(this, session, error); +} + +void Call::OnReceivedTerminateReason(Session *session, const std::string &reason) { + session_client_->session_manager()->signaling_thread()->Clear(this, + MSG_TERMINATECALL); + SignalReceivedTerminateReason(this, session, reason); +} + +} diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.h b/Plugins/jingle/libjingle/talk/session/phone/call.h new file mode 100644 index 0000000..7b8d9b6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/call.h @@ -0,0 +1,116 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CALL_H_ +#define _CALL_H_ + +#include "talk/base/messagequeue.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/client/socketmonitor.h" +#include "talk/xmpp/jid.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/session/phone/voicechannel.h" +#include "talk/session/phone/audiomonitor.h" + +#include <map> +#include <vector> +#include <deque> + +namespace cricket { + +class PhoneSessionClient; + +class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { +public: + Call(PhoneSessionClient *session_client); + ~Call(); + + Session *InitiateSession(const buzz::Jid &jid, + std::vector<buzz::XmlElement*>* extra_xml); + void AcceptSession(Session *session); + void RedirectSession(Session *session, const buzz::Jid &to); + void RejectSession(Session *session); + void TerminateSession(Session *session); + void Terminate(); + void StartConnectionMonitor(Session *session, int cms); + void StopConnectionMonitor(Session *session); + void StartAudioMonitor(Session *session, int cms); + void StopAudioMonitor(Session *session); + void Mute(bool mute); + + + const std::vector<Session *> &sessions(); + uint32 id(); + bool muted() const { return muted_; } + + // Setting this to false will cause the call to have a longer timeout and + // for the SignalSetupToCallVoicemail to never fire. + void set_send_to_voicemail(bool send_to_voicemail) { + send_to_voicemail_ = send_to_voicemail; + } + bool send_to_voicemail() { return send_to_voicemail_; } + + // Sets a flag on the chatapp that will redirect the call to voicemail once + // the call has been terminated + sigslot::signal0<> SignalSetupToCallVoicemail; + sigslot::signal2<Call *, Session *> SignalAddSession; + sigslot::signal2<Call *, Session *> SignalRemoveSession; + sigslot::signal3<Call *, Session *, Session::State> SignalSessionState; + sigslot::signal3<Call *, Session *, Session::Error> SignalSessionError; + sigslot::signal3<Call *, Session *, const std::string &> SignalReceivedTerminateReason; + sigslot::signal3<Call *, Session *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor; + sigslot::signal3<Call *, Session *, const AudioInfo&> SignalAudioMonitor; + sigslot::signal3<Call *, Session *, const MediaInfo&> SignalMediaMonitor; + +private: + void OnMessage(talk_base::Message *message); + void OnSessionState(Session *session, Session::State state); + void OnSessionError(Session *session, Session::Error error); + void OnReceivedTerminateReason(Session *session, const std::string &reason); + void AddSession(Session *session); + void RemoveSession(Session *session); + void EnableChannels(bool enable); + void Join(Call *call, bool enable); + void OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos); + void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info); + void OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info); + VoiceChannel* GetChannel(Session* session); + + uint32 id_; + PhoneSessionClient *session_client_; + std::vector<Session *> sessions_; + std::map<SessionID, VoiceChannel *> channel_map_; + bool muted_; + bool send_to_voicemail_; + + + friend class PhoneSessionClient; +}; + +} + +#endif // _CALL_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc new file mode 100644 index 0000000..77b7a73 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc @@ -0,0 +1,219 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_GIPS +#include "talk/session/phone/gipsmediaengine.h" +#elif HAVE_GIPSLITE +#include "talk/session/phone/gipslitemediaengine.h" +#else +#include "talk/session/phone/linphonemediaengine.h" +#endif +#include "channelmanager.h" +#include <cassert> +#include <iostream> +namespace cricket { + +const uint32 MSG_CREATEVOICECHANNEL = 1; +const uint32 MSG_DESTROYVOICECHANNEL = 2; +const uint32 MSG_SETAUDIOOPTIONS = 3; + +ChannelManager::ChannelManager(talk_base::Thread *worker_thread) { +#ifdef HAVE_GIPS + media_engine_ = new GipsMediaEngine(); +#elif HAVE_GIPSLITE + media_engine_ = new GipsLiteMediaEngine(); +#else + media_engine_ = new LinphoneMediaEngine(); +#endif + worker_thread_ = worker_thread; + initialized_ = false; + Init(); +} + +ChannelManager::~ChannelManager() { + Exit(); +} + +MediaEngine *ChannelManager::media_engine() { + return media_engine_; +} + +bool ChannelManager::Init() { + initialized_ = media_engine_->Init(); + return initialized_; +} + +void ChannelManager::Exit() { + if (!initialized_) + return; + + // Need to destroy the voice channels + + while (true) { + crit_.Enter(); + VoiceChannel *channel = NULL; + if (channels_.begin() != channels_.end()) + channel = channels_[0]; + crit_.Leave(); + if (channel == NULL) + break; + delete channel; + } + media_engine_->Terminate(); +} + +struct CreateParams { + Session *session; + VoiceChannel *channel; +}; + +VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) { + CreateParams params; + params.session = session; + params.channel = NULL; + talk_base::TypedMessageData<CreateParams *> data(¶ms); + worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data); + return params.channel; +} + +VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) { + talk_base::CritScope cs(&crit_); + + // This is ok to alloc from a thread other than the worker thread + assert(initialized_); + MediaChannel *channel = media_engine_->CreateChannel(); + if (channel == NULL) + return NULL; + + VoiceChannel *voice_channel = new VoiceChannel(this, session, channel); + channels_.push_back(voice_channel); + return voice_channel; +} + +void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) { + talk_base::TypedMessageData<VoiceChannel *> data(voice_channel); + worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data); +} + +void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) { + talk_base::CritScope cs(&crit_); + // Destroy voice channel. + assert(initialized_); + std::vector<VoiceChannel *>::iterator it = std::find(channels_.begin(), + channels_.end(), voice_channel); + assert(it != channels_.end()); + if (it == channels_.end()) + return; + + channels_.erase(it); + MediaChannel *channel = voice_channel->channel(); + delete voice_channel; + delete channel; +} + +void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device) { + AudioOptions options; + options.auto_gain_control = auto_gain_control; + options.wave_in_device = wave_in_device; + options.wave_out_device = wave_out_device; + talk_base::TypedMessageData<AudioOptions> data(options); + worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data); +} + +void ChannelManager::SetAudioOptions_w(AudioOptions options) { + assert(worker_thread_ == talk_base::Thread::Current()); + + // Set auto gain control on + if (media_engine_->SetAudioOptions( + options.auto_gain_control ? MediaEngine::AUTO_GAIN_CONTROL : 0) != 0) { + // TODO: We need to log these failures. + } + + // Set the audio devices + // This will fail if audio is already playing. Stop all of the media + // start it up again after changing the setting. + { + talk_base::CritScope cs(&crit_); + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->PauseMedia_w(); + } + + if (media_engine_->SetSoundDevices(options.wave_in_device, + options.wave_out_device) == -1) { + // TODO: We need to log these failures. + } + + for (VoiceChannels::iterator it = channels_.begin(); + it < channels_.end(); + ++it) { + (*it)->UnpauseMedia_w(); + } + } +} + +talk_base::Thread *ChannelManager::worker_thread() { + return worker_thread_; +} + +void ChannelManager::OnMessage(talk_base::Message *message) { + switch (message->message_id) { + case MSG_CREATEVOICECHANNEL: + { + talk_base::TypedMessageData<CreateParams *> *data + = static_cast<talk_base::TypedMessageData<CreateParams *> *>( + message->pdata); + data->data()->channel = CreateVoiceChannel_w(data->data()->session); + } + break; + + case MSG_DESTROYVOICECHANNEL: + { + talk_base::TypedMessageData<VoiceChannel *> *data + = static_cast<talk_base::TypedMessageData<VoiceChannel *> *>( + message->pdata); + DestroyVoiceChannel_w(data->data()); + } + break; + case MSG_SETAUDIOOPTIONS: + { + talk_base::TypedMessageData<AudioOptions> *data + = static_cast<talk_base::TypedMessageData<AudioOptions> *>( + message->pdata); + SetAudioOptions_w(data->data()); + } + break; + } +} + +} diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h new file mode 100644 index 0000000..c17d5bb --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CHANNELMANAGER_H_ +#define _CHANNELMANAGER_H_ + +#include "talk/base/thread.h" +#include "talk/base/criticalsection.h" +#include "talk/p2p/base/session.h" +#include "talk/session/phone/voicechannel.h" +#include "talk/session/phone/mediaengine.h" +#include <vector> + +namespace cricket { + +class VoiceChannel; + +class ChannelManager : public talk_base::MessageHandler { +public: + ChannelManager(talk_base::Thread *worker_thread); + ~ChannelManager(); + + VoiceChannel *CreateVoiceChannel(Session *session); + void DestroyVoiceChannel(VoiceChannel *voice_channel); + void SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device); + + MediaEngine *media_engine(); + talk_base::Thread *worker_thread(); + +private: + VoiceChannel *CreateVoiceChannel_w(Session *session); + void DestroyVoiceChannel_w(VoiceChannel *voice_channel); + void OnMessage(talk_base::Message *message); + bool Init(); + void Exit(); + + struct AudioOptions { + bool auto_gain_control; + int wave_in_device; + int wave_out_device; + }; + void SetAudioOptions_w(AudioOptions options); + + talk_base::Thread *worker_thread_; + MediaEngine *media_engine_; + bool initialized_; + talk_base::CriticalSection crit_; + + typedef std::vector<VoiceChannel*> VoiceChannels; + VoiceChannels channels_; +}; + +} + +#endif // _CHANNELMANAGER_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/codec.h b/Plugins/jingle/libjingle/talk/session/phone/codec.h new file mode 100644 index 0000000..ef99c2b --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/codec.h @@ -0,0 +1,47 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + + +#ifndef _CODEC_H_ +#define _CODEC_H_ + +struct Codec { + int id; + std::string name; + int clockrate; + int bitrate; + int channels; + + int preference; + + // Creates a codec with the given parameters. + Codec(int pt, const std::string& nm, int cr, int br, int cs, int pr) : + id(pt), name(nm), clockrate(cr), preference(pr), bitrate(br), channels(cs) {} + // Ranks codecs by their preferences. +}; + +#endif // CODEC_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc new file mode 100644 index 0000000..6609790 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc @@ -0,0 +1,239 @@ +/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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 name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+#include "talk/base/logging.h"
+#include <cassert>
+#include <iostream>
+#include "gipslitemediaengine.h"
+using namespace cricket;
+
+#if 0
+#define TRACK(x) LOG(LS_VERBOSE) << x
+#else
+#define TRACK(x)
+#endif
+
+//#define GIPS_TRACING
+
+namespace {
+struct CodecPref { const char* name; int clockrate; int pref; };
+const CodecPref kGIPSCodecPrefs[] = {
+ { "ISAC", 1600, 7 },
+ { "speex", 1600, 6 },
+ { "IPCMWB", 1600, 6},
+ { "speex", 8000, 4},
+ { "iLBC", 8000, 1 },
+ { "G723", 8000, 4 },
+ { "EG711U", 8000, 3 },
+ { "EG711A", 8000, 3 },
+ { "PCMU", 8000, 2 },
+ { "PCMA", 8000, 2 },
+ { "CN", 8000, 2 },
+ { "red", 8000, -1 },
+ { "telephone-event", 8000, -1 }
+};
+const size_t kNumGIPSCodecs = sizeof(kGIPSCodecPrefs) / sizeof(CodecPref);
+}
+
+
+void GipsLiteMediaChannel::SetCodecs(const std::vector<Codec> &codecs) {
+ GIPS_CodecInst c;
+ std::vector<Codec>::const_iterator i;
+
+
+ bool first = true;
+ for (i = codecs.begin(); i < codecs.end(); i++) {
+ if (engine_->FindGIPSCodec(*i, &c) == false)
+ continue;
+
+ if (c.pltype != i->id) {
+ c.pltype = i->id;
+ engine_->gips().GIPSVE_SetRecPayloadType(gips_channel_, &c);
+ }
+
+ if (first) {
+ LOG(LS_INFO) << "Using " << c.plname << "/" << c.plfreq;
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ first = false;
+ }
+ }
+ if (first) {
+ // We're being asked to set an empty list of codecs. This will only happen when
+ // dealing with a buggy client. We'll send them the most common format: PCMU
+ Codec codec(0, "PCMU", 8000, 0, 1, 0);
+ LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
+ engine_->FindGIPSCodec(codec, &c);
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ }
+}
+
+
+void GipsLiteMediaChannel::OnPacketReceived(const void *data, int len) {
+ engine_->gips().GIPSVE_ReceivedRTPPacket(gips_channel_, data, (int)len);
+}
+
+void GipsLiteMediaChannel::SetPlayout(bool playout) {
+ if (playout)
+ engine_->gips().GIPSVE_StartPlayout(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopPlayout(gips_channel_);
+}
+
+void GipsLiteMediaChannel::SetSend(bool send) {
+ if (send)
+ engine_->gips().GIPSVE_StartSend(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopSend(gips_channel_);
+}
+
+GipsLiteMediaChannel::GipsLiteMediaChannel(GipsLiteMediaEngine *engine) {
+ network_interface_ = NULL;
+ engine_ = engine;
+ gips_channel_ = engine_->gips().GIPSVE_CreateChannel();
+ engine_->gips().GIPSVE_SetSendTransport(gips_channel_, *this);
+}
+
+
+int GipsLiteMediaEngine::GetGIPSCodecPreference(const char *name, int clockrate) {
+ for (size_t i = 0; i < kNumGIPSCodecs; ++i) {
+ if ((strcmp(kGIPSCodecPrefs[i].name, name) == 0) &&
+ (kGIPSCodecPrefs[i].clockrate == clockrate))
+ return kGIPSCodecPrefs[i].pref;
+ }
+ assert(false);
+ return -1;
+}
+
+GipsLiteMediaEngine::GipsLiteMediaEngine() :
+ gips_(GetGipsVoiceEngineLite()) {}
+
+bool GipsLiteMediaEngine::Init() {
+
+ TRACK("GIPSVE_Init");
+ if (gips_.GIPSVE_Init() == -1)
+ return false;
+
+ char buffer[1024];
+ TRACK("GIPSVE_GetVersion");
+ int r = gips_.GIPSVE_GetVersion(buffer, sizeof(buffer));
+ LOG(LS_INFO) << "GIPS Version: " << r << ": " << buffer;
+
+ // Set auto gain control on
+ TRACK("GIPSVE_SetAGCStatus");
+ if (gips_.GIPSVE_SetAGCStatus(1) == -1)
+ return false;
+
+ TRACK("GIPSVE_GetNofCodecs");
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gips_codec;
+ if (gips_.GIPSVE_GetCodec(i, &gips_codec) >= 0) {
+ Codec codec(gips_codec.pltype, gips_codec.plname, gips_codec.plfreq, gips_codec.rate,
+ gips_codec.channels, GetGIPSCodecPreference(gips_codec.plname, gips_codec.plfreq));
+ LOG(LS_INFO) << gips_codec.plname << "/" << gips_codec.plfreq << "/" << gips_codec.channels << " " << gips_codec.pltype;
+ codecs_.push_back(codec);
+ }
+ }
+ return true;
+}
+
+void GipsLiteMediaEngine::Terminate() {
+ gips_.GIPSVE_Terminate();
+}
+
+MediaChannel * GipsLiteMediaEngine::CreateChannel() {
+ return new GipsLiteMediaChannel(this);
+}
+
+bool GipsLiteMediaEngine::FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec) {
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gc;
+ if (gips_.GIPSVE_GetCodec(i, &gc) >= 0) {
+ if (codec.id < 96) {
+ // Compare by id
+ if (codec.id != gc.pltype)
+ continue;
+ } else {
+ // Compare by name
+ if (strcmp(codec.name.c_str(), gc.plname) != 0)
+ continue;
+ }
+
+ // If the clockrate is specified, make sure it matches
+ if (codec.clockrate > 0 && codec.clockrate != gc.plfreq)
+ continue;
+
+ // If the bitrate is specified, make sure it matches
+ if (codec.bitrate > 0 && codec.bitrate != gc.rate)
+ continue;
+
+ // Make sure the channels match
+ if (codec.channels != gc.channels)
+ continue;
+
+ // If we got this far, we match.
+ if (gips_codec)
+ *gips_codec = gc;
+ return true;
+ }
+ }
+ return false;
+}
+
+int GipsLiteMediaEngine::SetAudioOptions(int options) {
+ // Set auto gain control on
+ if (gips_.GIPSVE_SetAGCStatus(options & AUTO_GAIN_CONTROL ? 1 : 0) == -1) {
+ return -1;
+ // TODO: We need to log these failures.
+ }
+ return 0;
+}
+
+int GipsLiteMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) {
+ if (gips_.GIPSVE_SetSoundDevices(wave_in_device, wave_out_device) == -1) {
+ int error = gips_.GIPSVE_GetLastError();
+ // TODO: We need to log these failures.
+ return error;
+ }
+ return 0;
+}
+
+bool GipsLiteMediaEngine::FindCodec(const Codec &codec)
+{
+ return FindGIPSCodec(codec, NULL);
+}
+
+std::vector<Codec> GipsLiteMediaEngine::codecs()
+{
+ return codecs_;
+}
\ No newline at end of file diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h new file mode 100644 index 0000000..56e1f2e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h @@ -0,0 +1,93 @@ +/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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 name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+
+#include "talk/third_party/gips/Interface/GipsVoiceEngineLite.h"
+#include "talk/third_party/gips/expiration.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class GipsLiteMediaEngine : public MediaEngine {
+ public:
+ GipsLiteMediaEngine();
+ ~GipsLiteMediaEngine() {}
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual int GetInputLevel() {return gips_.GIPSVE_GetInputLevel();}
+ virtual bool FindCodec(const Codec &codec);
+ virtual std::vector<Codec> codecs();
+ bool FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec);
+ GipsVoiceEngineLite &gips() {return gips_;};
+
+ private:
+ GipsVoiceEngineLite & gips_;
+ int GetGIPSCodecPreference(const char *name, int clockrate);
+ std::vector<Codec> codecs_;
+};
+
+class GipsLiteMediaChannel : public MediaChannel, public GIPS_transport {
+ public:
+ GipsLiteMediaChannel(GipsLiteMediaEngine *me);
+ virtual ~GipsLiteMediaChannel() {
+ StopMediaMonitor();
+ engine_->gips().GIPSVE_DeleteChannel(gips_channel_);
+ }
+ virtual void SetCodecs(const std::vector<Codec> &codecs);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+
+ virtual int GetOutputLevel() {return engine_->gips().GIPSVE_GetOutputLevel(gips_channel_);}
+ GipsLiteMediaEngine *engine();
+
+ virtual void StartMediaMonitor(VoiceChannel *voice_channel, uint32 cms) {}
+ virtual void StopMediaMonitor() {}
+
+ private:
+ GipsLiteMediaEngine *engine_;
+ int gips_channel_;
+
+ virtual int SendPacket(int channel, const void *data, int len) {if (network_interface_) network_interface_->SendPacket(data, len); return 1;}
+ virtual int SendRTCPPacket(int channel, const void *data, int len) {return 1;}
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc new file mode 100644 index 0000000..436e16e --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc @@ -0,0 +1,207 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// LinphoneMediaEngine is a Linphone implementation of MediaEngine +#include <mediastreamer2/mediastream.h> +//#ifdef HAVE_ILBC +//#include <mediastreamer2/msilbcdec.h> +//#endif +//#ifdef HAVE_SPEEX +//#include <mediastreamer2/msspeexdec.h> +//#endif + +#include <ortp/ortp.h> +#include <ortp/telephonyevents.h> +//#include <netdb.h> +//#include <unistd.h> +#include <fcntl.h> +#include <iostream> +#include "talk/base/logging.h" +#include "talk/base/thread.h" +#include "talk/base/helpers.h" +#include "talk/session/phone/codec.h" +#include "talk/session/phone/linphonemediaengine.h" + +using namespace cricket; + +void LinphoneMediaChannel::OnIncomingData(talk_base::AsyncSocket *s) +{ + char *buf[2048]; + int len; + len = s->Recv(buf, sizeof(buf)); + if (network_interface_ && !mute_) + network_interface_->SendPacket(buf, len); +} + +LinphoneMediaChannel::LinphoneMediaChannel(LinphoneMediaEngine*eng) : + pt_(-1), + audio_stream_(0), + engine_(eng) { + + talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread(); + talk_base::SocketServer *ss = thread->socketserver(); + socket_.reset(ss->CreateAsyncSocket(SOCK_DGRAM)); + + port_in_ = 2000 + CreateRandomId() % 1000; + port_out_ = 3000 + CreateRandomId() % 1000; + + socket_->Bind(talk_base::SocketAddress("localhost", port_out_)); + socket_->SignalReadEvent.connect(this, &LinphoneMediaChannel::OnIncomingData); +} + +LinphoneMediaChannel::~LinphoneMediaChannel() { + if (audio_stream_ != NULL) + audio_stream_stop(audio_stream_); +} + +void LinphoneMediaChannel::SetCodecs(const std::vector<Codec> &codecs) { + bool first = true; + std::vector<Codec>::const_iterator i; + + for (i = codecs.begin(); i < codecs.end(); i++) { + + if (!engine_->FindCodec(*i)) + continue; +#ifdef HAVE_ILBC + if (i->name == payload_type_ilbc.mime_type) { + rtp_profile_set_payload(&av_profile, i->id, &payload_type_ilbc); + } +#endif +#ifdef HAVE_SPEEX + if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) { + rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb); + } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) { + rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb); + } +#endif + + if (i->id == 0) + rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000); + + if (i->name == payload_type_telephone_event.mime_type) { + rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event); + } + + if (first) { + LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate; + pt_ = i->id; + audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, i->id, 250, false); + first = false; + } + } + + if (first) { + // We're being asked to set an empty list of codecs. This will only happen when + // working with a buggy client; let's try PCMU. + LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000"; + audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, 0, 250, false); + } + +} + +bool LinphoneMediaEngine::FindCodec(const Codec &c) { + if (c.id == 0) + return true; + if (c.name == payload_type_telephone_event.mime_type) + return true; +#ifdef HAVE_SPEEX + if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate) + return true; + if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate) + return true; +#endif +#ifdef HAVE_ILBC + if (c.name == payload_type_ilbc.mime_type) + return true; +#endif +return false; +} + +void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) { + uint8 buf[2048]; + memcpy(buf, data, len); + + /* We may receive packets with payload type 13: comfort noise. Linphone can't + * handle them, so let's ignore those packets. + */ + int payloadtype = buf[1] & 0x7f; + if (play_ && payloadtype != 13) + socket_->SendTo(buf, len, talk_base::SocketAddress("localhost", port_in_)); +} + +void LinphoneMediaChannel::SetPlayout(bool playout) { + play_ = playout; +} + +void LinphoneMediaChannel::SetSend(bool send) { + mute_ = !send; +} + +int LinphoneMediaChannel::GetOutputLevel() { return 0; } + +LinphoneMediaEngine::LinphoneMediaEngine() {} +LinphoneMediaEngine::~LinphoneMediaEngine() {} + +/* +static void null_log_handler(const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) { + LOG(LS_INFO) << log_domain << " " << message; +} +*/ + +bool LinphoneMediaEngine::Init() { +// g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, this); +// g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, this); +// g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, this); + ortp_init(); + ms_init(); + +#ifdef HAVE_SPEEX + //ms_speex_codec_init(); + + codecs_.push_back(Codec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8)); + codecs_.push_back(Codec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7)); + +#endif + +#ifdef HAVE_ILBC + //ms_ilbc_codec_init(); + codecs_.push_back(Codec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4)); +#endif + + codecs_.push_back(Codec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2)); + codecs_.push_back(Codec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1)); + return true; +} + +void LinphoneMediaEngine::Terminate() { + +} + +MediaChannel *LinphoneMediaEngine::CreateChannel() { + return new LinphoneMediaChannel(this); +} + +int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; } +int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; } + +float LinphoneMediaEngine::GetCurrentQuality() { return 0; } +int LinphoneMediaEngine::GetInputLevel() { return 0; } diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h new file mode 100644 index 0000000..8e57d43 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h @@ -0,0 +1,88 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// LinphoneMediaEngine is a Linphone implementation of MediaEngine + +#ifndef TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ +#define TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ + +#include <mediastreamer2/mediastream.h> +#include "talk/base/asyncsocket.h" +#include "talk/base/scoped_ptr.h" +#include "talk/session/phone/mediaengine.h" + +namespace cricket { + +class LinphoneMediaEngine; + +class LinphoneMediaChannel : public MediaChannel { + public: + LinphoneMediaChannel(LinphoneMediaEngine *eng); + virtual ~LinphoneMediaChannel(); + + virtual void SetCodecs(const std::vector<Codec> &codecs); + virtual void OnPacketReceived(const void *data, int len); + + virtual void SetPlayout(bool playout); + virtual void SetSend(bool send); + + + virtual int GetOutputLevel(); + bool mute() {return mute_;} + + virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) {} + virtual void StopMediaMonitor() {} + + private: + LinphoneMediaEngine *engine_; + AudioStream *audio_stream_; + talk_base::scoped_ptr<talk_base::AsyncSocket> socket_; + void OnIncomingData(talk_base::AsyncSocket *s); + int pt_; + bool mute_; + bool play_; + int port_in_; + int port_out_; +}; + +class LinphoneMediaEngine : public MediaEngine { + public: + LinphoneMediaEngine(); + ~LinphoneMediaEngine(); + virtual bool Init(); + virtual void Terminate(); + + virtual MediaChannel *CreateChannel(); + + virtual int SetAudioOptions(int options); + virtual int SetSoundDevices(int wave_in_device, int wave_out_device); + + virtual float GetCurrentQuality(); + virtual int GetInputLevel(); + + virtual std::vector<Codec, std::allocator<Codec> > codecs() {return codecs_;} + virtual bool FindCodec(const Codec&); + + private: + std::vector<Codec, std::allocator<Codec> > codecs_; +}; + +} // namespace cricket + +#endif // TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h new file mode 100644 index 0000000..65463a5 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h @@ -0,0 +1,78 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_ +#define TALK_SESSION_PHONE_MEDIACHANNEL_H_ + +#include "talk/base/basictypes.h" +#include "talk/base/sigslot.h" +#include "talk/session/phone/codec.h" + +namespace cricket { + +class VoiceChannel; + +struct MediaInfo { + unsigned short fraction_lost; + unsigned long cum_lost; + unsigned long ext_max; + unsigned long jitter; + int RTT; + int bytesSent; + int packetsSent; + int bytesReceived; + int packetsReceived; +}; + +class MediaChannel : public sigslot::has_slots<> { + public: + class NetworkInterface { + public: + virtual void SendPacket(const void *data, size_t len) = 0; + }; + MediaChannel() {network_interface_ = NULL;} + virtual ~MediaChannel() {}; + void SetInterface(NetworkInterface *iface) {network_interface_ = iface;} + virtual void SetCodecs(const std::vector<Codec> &codecs) = 0; + virtual void OnPacketReceived(const void *data, int len) = 0; + virtual void SetPlayout(bool playout) = 0; + virtual void SetSend(bool send) = 0; + virtual int GetOutputLevel() = 0; + + virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) = 0; + virtual void StopMediaMonitor() = 0; + sigslot::signal2<MediaChannel *, const MediaInfo &> SignalMediaMonitor; + + + NetworkInterface *network_interface() {return network_interface_;} + protected: + NetworkInterface *network_interface_; +}; + +}; // namespace cricket + +#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h new file mode 100644 index 0000000..e8081db --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h @@ -0,0 +1,70 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// MediaEngine is an abstraction of a media engine which can be subclassed +// to support different media componentry backends. + +#ifndef TALK_SESSION_PHONE_MEDIAENGINE_H_ +#define TALK_SESSION_PHONE_MEDIAENGINE_H_ + +#include <string> +#include <vector> + +#include "talk/session/phone/codec.h" +#include "talk/session/phone/mediachannel.h" + +namespace cricket { + +class MediaEngine { + public: + + MediaEngine() {} + + // Bitmask flags for options that may be supported by the media engine implementation + enum MediaEngineOptions { + AUTO_GAIN_CONTROL = 1 << 1, + }; + + // Initialize + virtual bool Init() = 0; + virtual void Terminate() = 0; + virtual MediaChannel *CreateChannel() = 0; + + virtual int SetAudioOptions(int options) = 0; + virtual int SetSoundDevices(int wave_in_device, int wave_out_device) = 0; + virtual int GetInputLevel() = 0; + + virtual std::vector<Codec> codecs() = 0; + + virtual bool FindCodec(const Codec &codec) = 0; + + int GetCodecPreference (Codec codec); +}; + +} // namespace cricket + +#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc new file mode 100644 index 0000000..60daf18 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc @@ -0,0 +1,278 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/logging.h" +#include "talk/session/phone/phonesessionclient.h" +#include "talk/xmpp/constants.h" +#include "talk/xmllite/qname.h" +namespace { + +const std::string NS_PHONE("http://www.google.com/session/phone"); +const std::string NS_EMPTY(""); + +const buzz::QName QN_PHONE_DESCRIPTION(true, NS_PHONE, "description"); +const buzz::QName QN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type"); +const buzz::QName QN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id"); +const buzz::QName QN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name"); +const buzz::QName QN_PHONE_PAYLOADTYPE_RATE(true, NS_EMPTY, "clockrate"); +const buzz::QName QN_PHONE_PAYLOADTYPE_BITRATE(true, NS_EMPTY, "bitrate"); +const buzz::QName QN_PHONE_PAYLOADTYPE_CHANNELS(true, NS_EMPTY, "channels"); +} + +namespace cricket { + +PhoneSessionClient::PhoneSessionClient( + const buzz::Jid& jid, SessionManager *manager) + : jid_(jid), session_manager_(manager) { + // No call to start, and certainly no call with focus + focus_call_ = NULL; + + // Start up the channel manager on a worker thread + channel_manager_ = new ChannelManager(session_manager_->worker_thread()); + + // Register ourselves as the handler of phone sessions. + session_manager_->AddClient(NS_PHONE, this); +} + + +PhoneSessionClient::~PhoneSessionClient() { + // Destroy all calls + std::map<uint32, Call *>::iterator it; + while (calls_.begin() != calls_.end()) { + std::map<uint32, Call *>::iterator it = calls_.begin(); + DestroyCall((*it).second); + } + + // Delete channel manager. This will wait for the channels to exit + delete channel_manager_; + + // Remove ourselves from the client map. + session_manager_->RemoveClient(NS_PHONE); +} + +PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() { + PhoneSessionDescription* session_desc = new PhoneSessionDescription(); + + + MediaEngine *me = channel_manager_->media_engine(); + std::vector<Codec> codecs = me->codecs(); + std::vector<Codec>::iterator i; + for (i = codecs.begin(); i < codecs.end(); i++) + session_desc->AddCodec(*i); + + session_desc->Sort(); + return session_desc; +} + +PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) { + const PhoneSessionDescription* offer_desc = + static_cast<const PhoneSessionDescription*>(offer); + PhoneSessionDescription* accept_desc = new PhoneSessionDescription(); + std::vector<Codec> codecs = channel_manager_->media_engine()->codecs(); + std::vector<Codec>::iterator iter; + for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) { + if (channel_manager_->media_engine()->FindCodec(offer_desc->codecs()[i])) + accept_desc->AddCodec(offer_desc->codecs()[i]); + } + + accept_desc->Sort(); + return accept_desc; +} + +const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) { + PhoneSessionDescription* desc = new PhoneSessionDescription(); + + const buzz::XmlElement* payload_type = element->FirstNamed(QN_PHONE_PAYLOADTYPE); + int num_payload_types = 0; + + while (payload_type) { + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_ID)) { + int id = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_ID).c_str()); + + std::string name = ""; + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_NAME)) + name = payload_type->Attr(QN_PHONE_PAYLOADTYPE_NAME); + + int clockrate = 0; + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_RATE)) + clockrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_RATE).c_str()); + + int bitrate = 0; + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_BITRATE)) + bitrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_BITRATE).c_str()); + + int channels = 1; + if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_CHANNELS)) + channels = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_CHANNELS).c_str()); + + desc->AddCodec(Codec(id, name, clockrate, bitrate, channels, 0)); + } + + payload_type = payload_type->NextNamed(QN_PHONE_PAYLOADTYPE); + num_payload_types += 1; + } + + // For backward compatability, we can assume the other client is (an old + // version of Talk) if it has no payload types at all. + if (num_payload_types == 0) { + desc->AddCodec(Codec(103, "ISAC", 16000, -1, 1, 1)); + desc->AddCodec(Codec(0, "PCMU", 8000, 64000, 1, 0)); + } + + return desc; +} + +buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) { + const PhoneSessionDescription* session_desc = + static_cast<const PhoneSessionDescription*>(_session_desc); + buzz::XmlElement* description = new buzz::XmlElement(QN_PHONE_DESCRIPTION, true); + + + for (size_t i = 0; i < session_desc->codecs().size(); ++i) { + buzz::XmlElement* payload_type = new buzz::XmlElement(QN_PHONE_PAYLOADTYPE, true); + + char buf[32]; + sprintf(buf, "%d", session_desc->codecs()[i].id); + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_ID, buf); + + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_NAME, + session_desc->codecs()[i].name.c_str()); + + if (session_desc->codecs()[i].clockrate > 0) { + sprintf(buf, "%d", session_desc->codecs()[i].clockrate); + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_RATE, buf); + } + + if (session_desc->codecs()[i].channels > 1) { + sprintf(buf, "%d", session_desc->codecs()[i].channels); + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_CHANNELS, buf); + } + + if (session_desc->codecs()[i].bitrate > 0) { + sprintf(buf, "%d", session_desc->codecs()[i].bitrate); + payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_BITRATE, buf); + } + + description->AddElement(payload_type); + } + + return description; +} + +Call *PhoneSessionClient::CreateCall() { + Call *call = new Call(this); + calls_[call->id()] = call; + SignalCallCreate(call); + return call; +} + +void PhoneSessionClient::OnSessionCreate(Session *session, + bool received_initiate) { + if (received_initiate) { + session->SignalState.connect(this, &PhoneSessionClient::OnSessionState); + + Call *call = CreateCall(); + session_map_[session->id()] = call; + call->AddSession(session); + } +} + +void PhoneSessionClient::OnSessionState(Session *session, + Session::State state) { + if (state == Session::STATE_RECEIVEDINITIATE) { + // If our accept would have no codecs, then we must reject this call. + PhoneSessionDescription* accept_desc = + CreateAcceptSessionDescription(session->remote_description()); + if (accept_desc->codecs().size() == 0) { + // TODO: include an error description with the rejection. + session->Reject(); + } + delete accept_desc; + } +} + +void PhoneSessionClient::DestroyCall(Call *call) { + // Change focus away, signal destruction + + if (call == focus_call_) + SetFocus(NULL); + SignalCallDestroy(call); + + // Remove it from calls_ map and delete + + std::map<uint32, Call *>::iterator it = calls_.find(call->id()); + if (it != calls_.end()) + calls_.erase(it); + + delete call; +} + +void PhoneSessionClient::OnSessionDestroy(Session *session) { + // Find the call this session is in, remove it + + std::map<SessionID, Call *>::iterator it = session_map_.find(session->id()); + assert(it != session_map_.end()); + if (it != session_map_.end()) { + Call *call = (*it).second; + session_map_.erase(it); + call->RemoveSession(session); + } +} + +Call *PhoneSessionClient::GetFocus() { + return focus_call_; +} + +void PhoneSessionClient::SetFocus(Call *call) { + Call *old_focus_call = focus_call_; + if (focus_call_ != call) { + if (focus_call_ != NULL) + focus_call_->EnableChannels(false); + focus_call_ = call; + if (focus_call_ != NULL) + focus_call_->EnableChannels(true); + SignalFocus(focus_call_, old_focus_call); + } +} + +void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) { + // Move all sessions from call to call_to_join, delete call. + // If call_to_join has focus, added sessions should have enabled channels. + + if (focus_call_ == call) + SetFocus(NULL); + call_to_join->Join(call, focus_call_ == call_to_join); + DestroyCall(call); +} + +Session *PhoneSessionClient::CreateSession(Call *call) { + Session *session = session_manager_->CreateSession(jid().Str(), NS_PHONE); + session_map_[session->id()] = call; + return session; +} + +} diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h new file mode 100644 index 0000000..35083c5 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h @@ -0,0 +1,132 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _PHONESESSIONCLIENT_H_ +#define _PHONESESSIONCLIENT_H_ + +#include "talk/session/phone/call.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/base/sigslot.h" +#include "talk/base/messagequeue.h" +#include "talk/base/thread.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessionclient.h" +#include "talk/p2p/base/sessiondescription.h" +#include <map> + +namespace cricket { + +class Call; +class PhoneSessionDescription; + +class PhoneSessionClient: public SessionClient, public sigslot::has_slots<> { +public: + PhoneSessionClient(const buzz::Jid& jid, SessionManager *manager); + ~PhoneSessionClient(); + + const buzz::Jid &jid() const { return jid_; } + SessionManager* session_manager() const { return session_manager_; } + ChannelManager* channel_manager() const { return channel_manager_; } + + Call *CreateCall(); + void DestroyCall(Call *call); + + Call *GetFocus(); + void SetFocus(Call *call); + + Call *GetCall(uint32 id) { + std::map<uint32, Call *>::iterator it = calls_.find(id); + if (it != calls_.end()) + return it->second; + return NULL; + } + + void JoinCalls(Call *call_to_join, Call *call); + + void SetAudioOptions(bool auto_gain_control, int wave_in_device, + int wave_out_device) { + if (channel_manager_) + channel_manager_->SetAudioOptions(auto_gain_control, wave_in_device, + wave_out_device); + } + + sigslot::signal2<Call *, Call *> SignalFocus; + sigslot::signal1<Call *> SignalCallCreate; + sigslot::signal1<Call *> SignalCallDestroy; + + PhoneSessionDescription* CreateOfferSessionDescription(); + + PhoneSessionDescription* CreateAcceptSessionDescription(const SessionDescription* offer); + + // Returns our preference for the given codec. + static int GetMediaCodecPreference(Codec codec); + +private: + void OnSessionCreate(Session *session, bool received_initiate); + void OnSessionState(Session *session, Session::State state); + void OnSessionDestroy(Session *session); + const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element); + buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description); + Session *CreateSession(Call *call); + + buzz::Jid jid_; + SessionManager* session_manager_; + Call *focus_call_; + ChannelManager *channel_manager_; + std::map<uint32, Call *> calls_; + std::map<SessionID, Call *> session_map_; + + friend class Call; +}; + +class PhoneSessionDescription: public SessionDescription { +public: + // Returns the list of codecs sorted by our preference. + const std::vector<Codec>& codecs() const { return codecs_; } + // Adds another codec to the list. + void AddCodec(const Codec& codec) { codecs_.push_back(codec); } + // Sorts the list of codecs by preference. + void Sort() { std::sort(codecs_.begin(), codecs_.end(), PreferenceSort()); } + +private: + std::vector<Codec> codecs_; + struct PreferenceSort { + bool operator()(Codec a, Codec b) { + return a.preference > b.preference; + } + }; + +#if defined(FEATURE_ENABLE_VOICEMAIL) + std::string lang_; +#endif + +}; + +} + +#endif // _PHONESESSIONCLIENT_H_ diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc new file mode 100644 index 0000000..58b8760 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc @@ -0,0 +1,330 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/session/phone/voicechannel.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/p2p/base/transportchannel.h" +#include "talk/session/phone/channelmanager.h" +#include "talk/session/phone/phonesessionclient.h" +#include <cassert> +#undef SetPort + +namespace cricket { + + +VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session, + MediaChannel *channel) { + channel_manager_ = manager; + assert(channel_manager_->worker_thread() == talk_base::Thread::Current()); + media_channel_ = channel; + session_ = session; + socket_monitor_ = NULL; + audio_monitor_ = NULL; + transport_channel_ = session_->CreateChannel("rtp"); + transport_channel_->SignalWritableState.connect( + this, &VoiceChannel::OnWritableState); + transport_channel_->SignalReadPacket.connect( + this, &VoiceChannel::OnChannelRead); + media_channel_->SetInterface(this); + enabled_ = false; + paused_ = false; + writable_ = false; + muted_ = false; + LOG(INFO) << "Created voice channel"; + + session->SignalState.connect(this, &VoiceChannel::OnSessionState); + OnSessionState(session, session->state()); +} + +VoiceChannel::~VoiceChannel() { + assert(channel_manager_->worker_thread() == talk_base::Thread::Current()); + enabled_ = false; + ChangeState(); + delete socket_monitor_; + delete audio_monitor_; + talk_base::Thread::Current()->Clear(this); + if (transport_channel_ != NULL) + session_->DestroyChannel(transport_channel_); + LOG(INFO) << "Destroyed voice channel"; +} + +void VoiceChannel::OnMessage(talk_base::Message *pmsg) { + switch (pmsg->message_id) { + case MSG_ENABLE: + EnableMedia_w(); + break; + + case MSG_DISABLE: + DisableMedia_w(); + break; + + case MSG_MUTE: + MuteMedia_w(); + break; + + case MSG_UNMUTE: + UnmuteMedia_w(); + break; + + case MSG_SETSENDCODEC: + SetSendCodec_w(); + break; + } +} + +void VoiceChannel::Enable(bool enable) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, + enable ? MSG_ENABLE : MSG_DISABLE); +} + +void VoiceChannel::Mute(bool mute) { + // Can be called from thread other than worker thread + channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE); +} + + +MediaChannel * VoiceChannel::channel() { + return media_channel_; +} + +void VoiceChannel::OnSessionState(Session* session, Session::State state) { + if ((state == Session::STATE_RECEIVEDACCEPT) || + (state == Session::STATE_RECEIVEDINITIATE)) { + channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC); + } +} + +void VoiceChannel::SetSendCodec_w() { + assert(channel_manager_->worker_thread() == talk_base::Thread::Current()); + + const PhoneSessionDescription* desc = + static_cast<const PhoneSessionDescription*>( + session()->remote_description()); + + media_channel_->SetCodecs(desc->codecs()); +} + +void VoiceChannel::OnWritableState(TransportChannel* channel) { + ASSERT(channel == transport_channel_); + if (transport_channel_->writable()) { + ChannelWritable_w(); + } else { + ChannelNotWritable_w(); + } +} + +void VoiceChannel::OnChannelRead(TransportChannel* channel, + const char* data, + size_t len) { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine + media_channel_->OnPacketReceived(data, (int)len); +} + +void VoiceChannel::SendPacket(const void *data, size_t len) { + // SendPacket gets called from MediaEngine; send to socket + // MediaEngine will call us on a random thread. The Send operation on the + // socket is special in that it can handle this. + transport_channel_->SendPacket(static_cast<const char *>(data), len); +} + +void VoiceChannel::ChangeState() { + if (paused_ || !enabled_ || !writable_) { + media_channel_->SetPlayout(false); + media_channel_->SetSend(false); + } else { + if (muted_) { + media_channel_->SetSend(false); + media_channel_->SetPlayout(true); + } else { + media_channel_->SetSend(true); + media_channel_->SetPlayout(true); + } + } +} + +void VoiceChannel::PauseMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + ASSERT(!paused_); + + LOG(INFO) << "Voice channel paused"; + paused_ = true; + ChangeState(); +} + +void VoiceChannel::UnpauseMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + ASSERT(paused_); + + LOG(INFO) << "Voice channel unpaused"; + paused_ = false; + ChangeState(); +} + +void VoiceChannel::EnableMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (enabled_) + return; + + LOG(INFO) << "Voice channel enabled"; + enabled_ = true; + ChangeState(); +} + +void VoiceChannel::DisableMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (!enabled_) + return; + + LOG(INFO) << "Voice channel disabled"; + enabled_ = false; + ChangeState(); +} + +void VoiceChannel::MuteMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (muted_) + return; + + LOG(INFO) << "Voice channel muted"; + muted_ = true; + ChangeState(); +} + +void VoiceChannel::UnmuteMedia_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (!muted_) + return; + + LOG(INFO) << "Voice channel unmuted"; + muted_ = false; + ChangeState(); +} + +void VoiceChannel::ChannelWritable_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (writable_) + return; + + LOG(INFO) << "Voice channel socket writable"; + writable_ = true; + ChangeState(); +} + +void VoiceChannel::ChannelNotWritable_w() { + ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current()); + if (!writable_) + return; + + LOG(INFO) << "Voice channel socket not writable"; + writable_ = false; + ChangeState(); +} + + +void VoiceChannel::StartConnectionMonitor(int cms) { + delete socket_monitor_; + socket_monitor_ = + new SocketMonitor(session_, transport_channel_, + talk_base::Thread::Current()); + socket_monitor_->SignalUpdate.connect( + this, &VoiceChannel::OnConnectionMonitorUpdate); + socket_monitor_->Start(cms); +} + +void VoiceChannel::StopConnectionMonitor() { + if (socket_monitor_ != NULL) { + socket_monitor_->Stop(); + socket_monitor_->SignalUpdate.disconnect(this); + delete socket_monitor_; + socket_monitor_ = NULL; + } +} + +void VoiceChannel::OnConnectionMonitorUpdate( + SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos) { + SignalConnectionMonitor(this, infos); +} + +void VoiceChannel::StartAudioMonitor(int cms) { + delete audio_monitor_; + audio_monitor_ = new AudioMonitor(this, talk_base::Thread::Current()); + audio_monitor_ + ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate); + audio_monitor_->Start(cms); +} + +void VoiceChannel::StopAudioMonitor() { + if (audio_monitor_ != NULL) { + audio_monitor_ ->Stop(); + audio_monitor_ ->SignalUpdate.disconnect(this); + delete audio_monitor_ ; + audio_monitor_ = NULL; + } +} + +void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor, + const AudioInfo& info) { + SignalAudioMonitor(this, info); +} + +void VoiceChannel::StartMediaMonitor(int cms) { + media_channel_ + ->SignalMediaMonitor.connect(this, &VoiceChannel::OnMediaMonitorUpdate); + media_channel_->StartMediaMonitor(this, cms); +} + +void VoiceChannel::StopMediaMonitor() { + media_channel_->SignalMediaMonitor.disconnect(this); + media_channel_->StopMediaMonitor(); +} + +void VoiceChannel::OnMediaMonitorUpdate( + MediaChannel *media_channel, const MediaInfo &info) { + ASSERT(media_channel == media_channel_); + SignalMediaMonitor(this, info); +} + +Session *VoiceChannel::session() { + return session_; +} + +int VoiceChannel::GetInputLevel_w() { + return channel_manager_->media_engine()->GetInputLevel(); +} + +int VoiceChannel::GetOutputLevel_w() { + return media_channel_->GetOutputLevel(); +} + +talk_base::Thread* VoiceChannel::worker_thread() { + return channel_manager_->worker_thread(); +} + +} diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h new file mode 100644 index 0000000..3d0be12 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h @@ -0,0 +1,137 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _VOICECHANNEL_H_ +#define _VOICECHANNEL_H_ + +#include "talk/base/asyncudpsocket.h" +#include "talk/base/network.h" +#include "talk/base/sigslot.h" +#include "talk/p2p/client/socketmonitor.h" +#include "talk/p2p/base/session.h" +#include "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/mediachannel.h" + +namespace cricket { + +const uint32 MSG_ENABLE = 1; +const uint32 MSG_DISABLE = 2; +const uint32 MSG_MUTE = 3; +const uint32 MSG_UNMUTE = 4; +const uint32 MSG_SETSENDCODEC = 5; + + +class ChannelManager; + +class VoiceChannel + : public talk_base::MessageHandler, public sigslot::has_slots<>, + public MediaChannel::NetworkInterface { + public: + VoiceChannel(ChannelManager *manager, Session *session, + MediaChannel *channel); + ~VoiceChannel(); + + void Enable(bool enable); + void Mute(bool mute); + + + MediaChannel *channel(); + Session *session(); + + // Monitoring + + void StartConnectionMonitor(int cms); + void StopConnectionMonitor(); + sigslot::signal2<VoiceChannel *, const std::vector<ConnectionInfo> &> + SignalConnectionMonitor; + + void StartAudioMonitor(int cms); + void StopAudioMonitor(); + sigslot::signal2<VoiceChannel *, const AudioInfo&> SignalAudioMonitor; + talk_base::Thread* worker_thread(); + + void StartMediaMonitor(int cms); + void StopMediaMonitor(); + sigslot::signal2<VoiceChannel *, const MediaInfo&> SignalMediaMonitor; + + // Pausing so that the ChannelManager can change the audio devices. These + // should only be called from the worker thread + void PauseMedia_w(); + void UnpauseMedia_w(); + + int GetInputLevel_w(); + int GetOutputLevel_w(); + + // MediaEngine calls this + virtual void SendPacket(const void *data, size_t len); + +private: + void ChangeState(); + void EnableMedia_w(); + void DisableMedia_w(); + void MuteMedia_w(); + void UnmuteMedia_w(); + void ChannelWritable_w(); + void ChannelNotWritable_w(); + + + void OnConnectionMonitorUpdate(SocketMonitor *monitor, + const std::vector<ConnectionInfo> &infos); + void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info); + void OnMediaMonitorUpdate(MediaChannel *media_channel, + const MediaInfo& info); + + // From MessageHandler + + void OnMessage(talk_base::Message *pmsg); + + // Setting the send codec based on the remote description. + void OnSessionState(Session* session, Session::State state); + void SetSendCodec_w(); + + // From TransportChannel + + void OnWritableState(TransportChannel* channel); + void OnChannelRead(TransportChannel* channel, const char *data, size_t len); + + + bool enabled_; + bool paused_; + bool writable_; + bool muted_; + MediaChannel *media_channel_; + Session *session_; + TransportChannel *transport_channel_; + ChannelManager *channel_manager_; + SocketMonitor *socket_monitor_; + AudioMonitor *audio_monitor_; +}; + +} + +#endif // _VOICECHANNEL_H_ diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am new file mode 100644 index 0000000..3a8d244 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am @@ -0,0 +1,7 @@ +libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \ + pseudotcpchannel.cc +noinst_HEADERS = tunnelsessionclient.h \ + pseudotcpchannel.h +noinst_LTLIBRARIES = libcricketsessiontunnel.la + +AM_CXXFLAGS = -DPOSIX diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in new file mode 100644 index 0000000..4df78cc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in @@ -0,0 +1,452 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = talk/session/tunnel +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcricketsessiontunnel_la_LIBADD = +am_libcricketsessiontunnel_la_OBJECTS = tunnelsessionclient.lo \ + pseudotcpchannel.lo +libcricketsessiontunnel_la_OBJECTS = \ + $(am_libcricketsessiontunnel_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libcricketsessiontunnel_la_SOURCES) +DIST_SOURCES = $(libcricketsessiontunnel_la_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPAT_LIBS = @EXPAT_LIBS@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GIPS_FALSE = @GIPS_FALSE@ +GIPS_TRUE = @GIPS_TRUE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +ILBC_CFLAGS = @ILBC_CFLAGS@ +ILBC_LIBS = @ILBC_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MEDIA_LIBS = @MEDIA_LIBS@ +OBJEXT = @OBJEXT@ +ORTP_CFLAGS = @ORTP_CFLAGS@ +ORTP_LIBS = @ORTP_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PHONE_FALSE = @PHONE_FALSE@ +PHONE_TRUE = @PHONE_TRUE@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \ + pseudotcpchannel.cc + +noinst_HEADERS = tunnelsessionclient.h \ + pseudotcpchannel.h + +noinst_LTLIBRARIES = libcricketsessiontunnel.la +AM_CXXFLAGS = -DPOSIX +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/tunnel/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu talk/session/tunnel/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libcricketsessiontunnel.la: $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_DEPENDENCIES) + $(CXXLINK) $(libcricketsessiontunnel_la_LDFLAGS) $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pseudotcpchannel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tunnelsessionclient.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc new file mode 100644 index 0000000..54515c0 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc @@ -0,0 +1,554 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/basictypes.h" +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/stringutils.h" +#include "talk/p2p/base/transportchannel.h" +#include "pseudotcpchannel.h" + +using namespace talk_base; + +namespace cricket { + +extern const talk_base::ConstantLabel SESSION_STATES[]; + +// MSG_WK_* - worker thread messages +// MSG_ST_* - stream thread messages +// MSG_SI_* - signal thread messages + +enum { + MSG_WK_CLOCK = 1, + MSG_WK_PURGE, + MSG_ST_EVENT, + MSG_SI_DESTROYCHANNEL, + MSG_SI_DESTROY, +}; + +struct EventData : public MessageData { + int event, error; + EventData(int ev, int err = 0) : event(ev), error(err) { } +}; + +/////////////////////////////////////////////////////////////////////////////// +// PseudoTcpChannel::InternalStream +/////////////////////////////////////////////////////////////////////////////// + +class PseudoTcpChannel::InternalStream : public StreamInterface { +public: + InternalStream(PseudoTcpChannel* parent); + virtual ~InternalStream(); + + virtual StreamState GetState() const; + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + virtual void Close(); + + virtual bool GetSize(size_t* size) const { return false; } + virtual bool ReserveSize(size_t size) { return true; } + virtual bool Rewind() { return false; } + +private: + // parent_ is accessed and modified exclusively on the event thread, to + // avoid thread contention. This means that the PseudoTcpChannel cannot go + // away until after it receives a Close() from TunnelStream. + PseudoTcpChannel* parent_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// PseudoTcpChannel +// Member object lifetime summaries: +// session_ - passed in constructor, cleared when channel_ goes away. +// channel_ - created in Connect, destroyed when session_ or tcp_ goes away. +// tcp_ - created in Connect, destroyed when channel_ goes away, or connection +// closes. +// worker_thread_ - created when channel_ is created, purged when channel_ is +// destroyed. +// stream_ - created in GetStream, destroyed by owner at arbitrary time. +// this - created in constructor, destroyed when worker_thread_ and stream_ +// are both gone. +/////////////////////////////////////////////////////////////////////////////// + +// +// Signal thread methods +// + +PseudoTcpChannel::PseudoTcpChannel(Thread* stream_thread, Session* session) + : signal_thread_(session->session_manager()->signaling_thread()), + worker_thread_(NULL), + stream_thread_(stream_thread), + session_(session), channel_(NULL), tcp_(NULL), stream_(NULL), + stream_readable_(false), pending_read_event_(false), + ready_to_connect_(false) { + ASSERT(signal_thread_->IsCurrent()); +} + +PseudoTcpChannel::~PseudoTcpChannel() { + ASSERT(signal_thread_->IsCurrent()); + ASSERT(worker_thread_ == NULL); + ASSERT(session_ == NULL); + ASSERT(channel_ == NULL); + ASSERT(stream_ == NULL); + ASSERT(tcp_ == NULL); +} + +bool PseudoTcpChannel::Connect(const std::string& channel_name) { + ASSERT(signal_thread_->IsCurrent()); + CritScope lock(&cs_); + + if (channel_) + return false; + + ASSERT(session_ != NULL); + worker_thread_ = session_->session_manager()->worker_thread(); + channel_ = session_->CreateChannel(channel_name); + channel_name_ = channel_name; + channel_->SetOption(Socket::OPT_DONTFRAGMENT, 1); + + channel_->SignalDestroyed.connect(this, + &PseudoTcpChannel::OnChannelDestroyed); + channel_->SignalWritableState.connect(this, + &PseudoTcpChannel::OnChannelWritableState); + channel_->SignalReadPacket.connect(this, + &PseudoTcpChannel::OnChannelRead); + channel_->SignalRouteChange.connect(this, + &PseudoTcpChannel::OnChannelConnectionChanged); + + ASSERT(tcp_ == NULL); + tcp_ = new PseudoTcp(this, 0); + if (session_->initiator()) { + // Since we may try several protocols and network adapters that won't work, + // waiting until we get our first writable notification before initiating + // TCP negotiation. + ready_to_connect_ = true; + } + + return true; +} + +StreamInterface* PseudoTcpChannel::GetStream() { + ASSERT(signal_thread_->IsCurrent()); + CritScope lock(&cs_); + ASSERT(NULL != session_); + if (!stream_) + stream_ = new PseudoTcpChannel::InternalStream(this); + //TODO("should we disallow creation of new stream at some point?"); + return stream_; +} + +void PseudoTcpChannel::OnChannelDestroyed(TransportChannel* channel) { + LOG_F(LS_INFO) << "(" << channel->name() << ")"; + ASSERT(signal_thread_->IsCurrent()); + CritScope lock(&cs_); + ASSERT(channel == channel_); + signal_thread_->Clear(this, MSG_SI_DESTROYCHANNEL); + // When MSG_WK_PURGE is received, we know there will be no more messages from + // the worker thread. + worker_thread_->Clear(this, MSG_WK_CLOCK); + worker_thread_->Post(this, MSG_WK_PURGE); + session_ = NULL; + channel_ = NULL; + if ((stream_ != NULL) + && ((tcp_ == NULL) || (tcp_->State() != PseudoTcp::TCP_CLOSED))) + stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, 0)); + if (tcp_) { + tcp_->Close(true); + AdjustClock(); + } +} + +// +// Stream thread methods +// + +StreamState PseudoTcpChannel::GetState() const { + ASSERT(stream_ != NULL && stream_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!tcp_) + return SS_OPENING; + switch (tcp_->State()) { + case PseudoTcp::TCP_LISTEN: + case PseudoTcp::TCP_SYN_SENT: + case PseudoTcp::TCP_SYN_RECEIVED: + return SS_OPENING; + case PseudoTcp::TCP_ESTABLISHED: + return SS_OPEN; + case PseudoTcp::TCP_CLOSED: + default: + return SS_CLOSED; + } +} + +StreamResult PseudoTcpChannel::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + ASSERT(stream_ != NULL && stream_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!tcp_) + return SR_BLOCK; + + stream_readable_ = false; + int result = tcp_->Recv(static_cast<char*>(buffer), buffer_len); + //LOG_F(LS_VERBOSE) << "Recv returned: " << result; + if (result > 0) { + if (read) + *read = result; + // PseudoTcp doesn't currently support repeated Readable signals. Simulate + // them here. + stream_readable_ = true; + if (!pending_read_event_) { + pending_read_event_ = true; + stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ), true); + } + return SR_SUCCESS; + } else if (IsBlockingError(tcp_->GetError())) { + return SR_BLOCK; + } else { + if (error) + *error = tcp_->GetError(); + return SR_ERROR; + } + AdjustClock(); +} + +StreamResult PseudoTcpChannel::Write(const void* data, size_t data_len, + size_t* written, int* error) { + ASSERT(stream_ != NULL && stream_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!tcp_) + return SR_BLOCK; + int result = tcp_->Send(static_cast<const char*>(data), data_len); + //LOG_F(LS_VERBOSE) << "Send returned: " << result; + if (result > 0) { + if (written) + *written = result; + return SR_SUCCESS; + } else if (IsBlockingError(tcp_->GetError())) { + return SR_BLOCK; + } else { + if (error) + *error = tcp_->GetError(); + return SR_ERROR; + } + AdjustClock(); +} + +void PseudoTcpChannel::Close() { + ASSERT(stream_ != NULL && stream_thread_->IsCurrent()); + CritScope lock(&cs_); + stream_ = NULL; + // Clear out any pending event notifications + stream_thread_->Clear(this, MSG_ST_EVENT); + if (tcp_) { + tcp_->Close(false); + AdjustClock(); + } else { + CheckDestroy(); + } +} + +// +// Worker thread methods +// + +void PseudoTcpChannel::OnChannelWritableState(TransportChannel* channel) { + LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]"; + ASSERT(worker_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!channel_) { + LOG_F(LS_WARNING) << "NULL channel"; + return; + } + ASSERT(channel == channel_); + if (!tcp_) { + LOG_F(LS_WARNING) << "NULL tcp"; + return; + } + if (!ready_to_connect_ || !channel->writable()) + return; + + ready_to_connect_ = false; + tcp_->Connect(); + AdjustClock(); +} + +void PseudoTcpChannel::OnChannelRead(TransportChannel* channel, + const char* data, size_t size) { + //LOG_F(LS_VERBOSE) << "(" << size << ")"; + ASSERT(worker_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!channel_) { + LOG_F(LS_WARNING) << "NULL channel"; + return; + } + ASSERT(channel == channel_); + if (!tcp_) { + LOG_F(LS_WARNING) << "NULL tcp"; + return; + } + tcp_->NotifyPacket(data, size); + AdjustClock(); +} + +void PseudoTcpChannel::OnChannelConnectionChanged(TransportChannel* channel, + const SocketAddress& addr) { + LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]"; + ASSERT(worker_thread_->IsCurrent()); + CritScope lock(&cs_); + if (!channel_) { + LOG_F(LS_WARNING) << "NULL channel"; + return; + } + ASSERT(channel == channel_); + if (!tcp_) { + LOG_F(LS_WARNING) << "NULL tcp"; + return; + } + + scoped_ptr<Socket> mtu_socket( + worker_thread_->socketserver() + ->CreateSocket(SOCK_DGRAM)); + + uint16 mtu = 65535; + if (mtu_socket->Connect(addr) < 0) { + LOG_F(LS_ERROR) << "Socket::Connect: " << mtu_socket->GetError(); + } else if (mtu_socket->EstimateMTU(&mtu) < 0) { + LOG_F(LS_ERROR) << "Socket::EstimateMTU: " << mtu_socket->GetError(); + } else { + tcp_->NotifyMTU(mtu); + AdjustClock(); + } +} + +void PseudoTcpChannel::OnTcpOpen(PseudoTcp* tcp) { + LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]"; + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(worker_thread_->IsCurrent()); + ASSERT(tcp == tcp_); + if (stream_) { + stream_readable_ = true; + pending_read_event_ = true; + stream_thread_->Post(this, MSG_ST_EVENT, + new EventData(SE_OPEN | SE_READ | SE_WRITE)); + } +} + +void PseudoTcpChannel::OnTcpReadable(PseudoTcp* tcp) { + //LOG_F(LS_VERBOSE); + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(worker_thread_->IsCurrent()); + ASSERT(tcp == tcp_); + if (stream_) { + stream_readable_ = true; + if (!pending_read_event_) { + pending_read_event_ = true; + stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ)); + } + } +} + +void PseudoTcpChannel::OnTcpWriteable(PseudoTcp* tcp) { + //LOG_F(LS_VERBOSE); + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(worker_thread_->IsCurrent()); + ASSERT(tcp == tcp_); + if (stream_) + stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_WRITE)); +} + +void PseudoTcpChannel::OnTcpClosed(PseudoTcp* tcp, uint32 nError) { + LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]"; + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(worker_thread_->IsCurrent()); + ASSERT(tcp == tcp_); + if (stream_) + stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, nError)); +} + +// +// Multi-thread methods +// + +void PseudoTcpChannel::OnMessage(Message* pmsg) { + if (pmsg->message_id == MSG_WK_CLOCK) { + + ASSERT(worker_thread_->IsCurrent()); + //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_WK_CLOCK)"; + CritScope lock(&cs_); + if (tcp_) { + tcp_->NotifyClock(PseudoTcp::Now()); + AdjustClock(false); + } + + } else if (pmsg->message_id == MSG_WK_PURGE) { + + ASSERT(worker_thread_->IsCurrent()); + LOG_F(LS_INFO) << "(MSG_WK_PURGE)"; + // At this point, we know there are no additional worker thread messages. + CritScope lock(&cs_); + ASSERT(NULL == session_); + ASSERT(NULL == channel_); + worker_thread_ = NULL; + CheckDestroy(); + + } else if (pmsg->message_id == MSG_ST_EVENT) { + + ASSERT(stream_thread_->IsCurrent()); + //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_ST_EVENT, " + // << data->event << ", " << data->error << ")"; + ASSERT(stream_ != NULL); + EventData* data = static_cast<EventData*>(pmsg->pdata); + if (data->event & SE_READ) { + CritScope lock(&cs_); + pending_read_event_ = false; + } + stream_->SignalEvent(stream_, data->event, data->error); + delete data; + + } else if (pmsg->message_id == MSG_SI_DESTROYCHANNEL) { + + ASSERT(signal_thread_->IsCurrent()); + LOG_F(LS_INFO) << "(MSG_SI_DESTROYCHANNEL)"; + ASSERT(session_ != NULL); + ASSERT(channel_ != NULL); + session_->DestroyChannel(channel_); + + } else if (pmsg->message_id == MSG_SI_DESTROY) { + + ASSERT(signal_thread_->IsCurrent()); + LOG_F(LS_INFO) << "(MSG_SI_DESTROY)"; + // The message queue is empty, so it is safe to destroy ourselves. + delete this; + + } else { + ASSERT(false); + } +} + +IPseudoTcpNotify::WriteResult PseudoTcpChannel::TcpWritePacket( + PseudoTcp* tcp, const char* buffer, size_t len) { + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(tcp == tcp_); + ASSERT(NULL != channel_); + int sent = channel_->SendPacket(buffer, len); + if (sent > 0) { + //LOG_F(LS_VERBOSE) << "(" << sent << ") Sent"; + return IPseudoTcpNotify::WR_SUCCESS; + } else if (IsBlockingError(channel_->GetError())) { + LOG_F(LS_VERBOSE) << "Blocking"; + return IPseudoTcpNotify::WR_SUCCESS; + } else if (channel_->GetError() == EMSGSIZE) { + LOG_F(LS_ERROR) << "EMSGSIZE"; + return IPseudoTcpNotify::WR_TOO_LARGE; + } else { + PLOG(LS_ERROR, channel_->GetError()) << "PseudoTcpChannel::TcpWritePacket"; + ASSERT(false); + return IPseudoTcpNotify::WR_FAIL; + } +} + +void PseudoTcpChannel::AdjustClock(bool clear) { + ASSERT(cs_.CurrentThreadIsOwner()); + ASSERT(NULL != tcp_); + + long timeout = 0; + if (tcp_->GetNextClock(PseudoTcp::Now(), timeout)) { + ASSERT(NULL != channel_); + // Reset the next clock, by clearing the old and setting a new one. + if (clear) + worker_thread_->Clear(this, MSG_WK_CLOCK); + worker_thread_->PostDelayed(_max(timeout, 0L), this, MSG_WK_CLOCK); + return; + } + + delete tcp_; + tcp_ = NULL; + ready_to_connect_ = false; + + if (channel_) { + // If TCP has failed, no need for channel_ anymore + signal_thread_->Post(this, MSG_SI_DESTROYCHANNEL); + } +} + +void PseudoTcpChannel::CheckDestroy() { + ASSERT(cs_.CurrentThreadIsOwner()); + if ((worker_thread_ != NULL) || (stream_ != NULL)) + return; + signal_thread_->Post(this, MSG_SI_DESTROY); +} + +/////////////////////////////////////////////////////////////////////////////// +// PseudoTcpChannel::InternalStream +/////////////////////////////////////////////////////////////////////////////// + +PseudoTcpChannel::InternalStream::InternalStream(PseudoTcpChannel* parent) + : parent_(parent) { +} + +PseudoTcpChannel::InternalStream::~InternalStream() { + Close(); +} + +StreamState PseudoTcpChannel::InternalStream::GetState() const { + if (!parent_) + return SS_CLOSED; + return parent_->GetState(); +} + +StreamResult PseudoTcpChannel::InternalStream::Read( + void* buffer, size_t buffer_len, size_t* read, int* error) { + if (!parent_) { + if (error) + *error = ENOTCONN; + return SR_ERROR; + } + return parent_->Read(buffer, buffer_len, read, error); +} + +StreamResult PseudoTcpChannel::InternalStream::Write( + const void* data, size_t data_len, size_t* written, int* error) { + if (!parent_) { + if (error) + *error = ENOTCONN; + return SR_ERROR; + } + return parent_->Write(data, data_len, written, error); +} + +void PseudoTcpChannel::InternalStream::Close() { + if (!parent_) + return; + parent_->Close(); + parent_ = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h new file mode 100644 index 0000000..27574ea --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h @@ -0,0 +1,125 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __PSEUDOTCPCHANNEL_H__ +#define __PSEUDOTCPCHANNEL_H__ + +#include "talk/base/criticalsection.h" +#include "talk/base/messagequeue.h" +#include "talk/base/stream.h" +#include "talk/p2p/base/pseudotcp.h" +#include "talk/p2p/base/session.h" + +namespace talk_base { +class Thread; +} + +namespace cricket { + +class TransportChannel; + +/////////////////////////////////////////////////////////////////////////////// +// ChannelStream +// Note: The lifetime of TunnelSession is complicated. It needs to survive +// until the following three conditions are true: +// 1) TunnelStream has called Close (tracked via non-null stream_) +// 2) PseudoTcp has completed (tracked via non-null tcp_) +// 3) Session has been destroyed (tracked via non-null session_) +// This is accomplished by calling CheckDestroy after these indicators change. +/////////////////////////////////////////////////////////////////////////////// +// TunnelStream +// Note: Because TunnelStream provides a stream interface, it's lifetime is +// controlled by the owner of the stream pointer. As a result, we must support +// both the TunnelSession disappearing before TunnelStream, and vice versa. +/////////////////////////////////////////////////////////////////////////////// + +class PseudoTcpChannel + : public IPseudoTcpNotify, + public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + // Signal thread methods + PseudoTcpChannel(talk_base::Thread* stream_thread, + Session* session); + + bool Connect(const std::string& channel_name); + talk_base::StreamInterface* GetStream(); + + sigslot::signal1<PseudoTcpChannel*> SignalChannelClosed; + +private: + class InternalStream; + friend class InternalStream; + + virtual ~PseudoTcpChannel(); + + // Stream thread methods + talk_base::StreamState GetState() const; + talk_base::StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + talk_base::StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + void Close(); + + // Multi-thread methods + void OnMessage(talk_base::Message* pmsg); + void AdjustClock(bool clear = true); + void CheckDestroy(); + + // Signal thread methods + void OnChannelDestroyed(TransportChannel* channel); + + // Worker thread methods + void OnChannelWritableState(TransportChannel* channel); + void OnChannelRead(TransportChannel* channel, const char* data, size_t size); + void OnChannelConnectionChanged(TransportChannel* channel, + const talk_base::SocketAddress& addr); + + virtual void OnTcpOpen(PseudoTcp* ptcp); + virtual void OnTcpReadable(PseudoTcp* ptcp); + virtual void OnTcpWriteable(PseudoTcp* ptcp); + virtual void OnTcpClosed(PseudoTcp* ptcp, uint32 nError); + virtual IPseudoTcpNotify::WriteResult TcpWritePacket(PseudoTcp* tcp, + const char* buffer, + size_t len); + + talk_base::Thread* signal_thread_, * worker_thread_, * stream_thread_; + Session* session_; + TransportChannel* channel_; + std::string channel_name_; + PseudoTcp* tcp_; + InternalStream* stream_; + bool stream_readable_, pending_read_event_; + bool ready_to_connect_; + mutable talk_base::CriticalSection cs_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket + +#endif // __PSEUDOTCPCHANNEL_H__ diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc new file mode 100644 index 0000000..855f0db --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc @@ -0,0 +1,316 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/basicdefs.h" +#include "talk/base/basictypes.h" +#include "talk/base/common.h" +#include "talk/base/helpers.h" +#include "talk/base/logging.h" +#include "talk/base/stringutils.h" +#include "talk/p2p/base/transportchannel.h" +#include "talk/xmllite/xmlelement.h" +#include "pseudotcpchannel.h" +#include "tunnelsessionclient.h" + +namespace cricket { + +const std::string NS_TUNNEL("http://www.google.com/talk/tunnel"); +const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description"); +const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type"); + +enum { + MSG_CLOCK = 1, + MSG_DESTROY, + MSG_TERMINATE, + MSG_EVENT, + MSG_CREATE_TUNNEL, +}; + +struct EventData : public talk_base::MessageData { + int event, error; + EventData(int ev, int err = 0) : event(ev), error(err) { } +}; + +struct CreateTunnelData : public talk_base::MessageData { + buzz::Jid jid; + std::string description; + talk_base::Thread* thread; + talk_base::StreamInterface* stream; +}; + +extern const talk_base::ConstantLabel SESSION_STATES[]; + +const talk_base::ConstantLabel SESSION_STATES[] = { + KLABEL(Session::STATE_INIT), + KLABEL(Session::STATE_SENTINITIATE), + KLABEL(Session::STATE_RECEIVEDINITIATE), + KLABEL(Session::STATE_SENTACCEPT), + KLABEL(Session::STATE_RECEIVEDACCEPT), + KLABEL(Session::STATE_SENTMODIFY), + KLABEL(Session::STATE_RECEIVEDMODIFY), + KLABEL(Session::STATE_SENTREJECT), + KLABEL(Session::STATE_RECEIVEDREJECT), + KLABEL(Session::STATE_SENTREDIRECT), + KLABEL(Session::STATE_SENTTERMINATE), + KLABEL(Session::STATE_RECEIVEDTERMINATE), + KLABEL(Session::STATE_INPROGRESS), + KLABEL(Session::STATE_DEINIT), + LASTLABEL +}; + +/////////////////////////////////////////////////////////////////////////////// +// TunnelSessionDescription +/////////////////////////////////////////////////////////////////////////////// + +struct TunnelSessionDescription : public SessionDescription { + std::string description; + + TunnelSessionDescription(const std::string& desc) : description(desc) { } +}; + +/////////////////////////////////////////////////////////////////////////////// +// TunnelSessionClient +/////////////////////////////////////////////////////////////////////////////// + +TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid, + SessionManager* manager) + : jid_(jid), session_manager_(manager), shutdown_(false) { + // Register ourselves as the handler of tunnel sessions. + session_manager_->AddClient(NS_TUNNEL, this); +} + +TunnelSessionClient::~TunnelSessionClient() { + shutdown_ = true; + for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); + it != sessions_.end(); + ++it) { + Session* session = (*it)->ReleaseSession(true); + session_manager_->DestroySession(session); + } + session_manager_->RemoveClient(NS_TUNNEL); +} + +const SessionDescription* TunnelSessionClient::CreateSessionDescription( + const buzz::XmlElement* element) { + if (const buzz::XmlElement* type_elem = element->FirstNamed(QN_TUNNEL_TYPE)) { + return new TunnelSessionDescription(type_elem->BodyText()); + } + ASSERT(false); + return 0; +} + +buzz::XmlElement* TunnelSessionClient::TranslateSessionDescription( + const SessionDescription* description) { + const TunnelSessionDescription* desc = + static_cast<const TunnelSessionDescription*>(description); + + buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true); + buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE); + type_elem->SetBodyText(desc->description); + root->AddElement(type_elem); + return root; +} + +void TunnelSessionClient::OnSessionCreate(Session* session, bool received) { + LOG(LS_INFO) << "TunnelSessionClient::OnSessionCreate: received=" << received; + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (received) + sessions_.push_back( + new TunnelSession(this, session, talk_base::Thread::Current())); +} + +void TunnelSessionClient::OnSessionDestroy(Session* session) { + LOG(LS_INFO) << "TunnelSessionClient::OnSessionDestroy"; + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + if (shutdown_) + return; + for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); + it != sessions_.end(); + ++it) { + if ((*it)->HasSession(session)) { + VERIFY((*it)->ReleaseSession(false) == session); + sessions_.erase(it); + return; + } + } +} + +talk_base::StreamInterface* TunnelSessionClient::CreateTunnel( + const buzz::Jid& to, const std::string& description) { + // Valid from any thread + CreateTunnelData data; + data.jid = to; + data.description = description; + data.thread = talk_base::Thread::Current(); + session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data); + return data.stream; +} + +talk_base::StreamInterface* TunnelSessionClient::AcceptTunnel( + Session* session) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + TunnelSession* tunnel = NULL; + for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); + it != sessions_.end(); + ++it) { + if ((*it)->HasSession(session)) { + tunnel = *it; + break; + } + } + ASSERT(tunnel != NULL); + + const TunnelSessionDescription* in_desc = + static_cast<const TunnelSessionDescription*>( + session->remote_description()); + TunnelSessionDescription* out_desc = new TunnelSessionDescription( + in_desc->description); + session->Accept(out_desc); + return tunnel->GetStream(); +} + +void TunnelSessionClient::DeclineTunnel(Session* session) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + session->Reject(); +} + +void TunnelSessionClient::OnMessage(talk_base::Message* pmsg) { + if (pmsg->message_id == MSG_CREATE_TUNNEL) { + ASSERT(session_manager_->signaling_thread()->IsCurrent()); + CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata); + Session* session = session_manager_->CreateSession(jid_.Str(), NS_TUNNEL); + TunnelSession* tunnel = new TunnelSession(this, session, data->thread); + sessions_.push_back(tunnel); + TunnelSessionDescription* desc = new TunnelSessionDescription(data->description); + session->Initiate(data->jid.Str(), NULL, desc); + data->stream = tunnel->GetStream(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// TunnelSession +/////////////////////////////////////////////////////////////////////////////// + +// +// Signalling thread methods +// + +TunnelSession::TunnelSession(TunnelSessionClient* client, Session* session, + talk_base::Thread* stream_thread) + : client_(client), session_(session), channel_(NULL) { + ASSERT(client_ != NULL); + ASSERT(session_ != NULL); + session_->SignalState.connect(this, &TunnelSession::OnSessionState); + channel_ = new PseudoTcpChannel(stream_thread, session_); + channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed); +} + +TunnelSession::~TunnelSession() { + ASSERT(client_ != NULL); + ASSERT(session_ == NULL); + ASSERT(channel_ == NULL); +} + +talk_base::StreamInterface* TunnelSession::GetStream() { + ASSERT(channel_ != NULL); + return channel_->GetStream(); +} + +bool TunnelSession::HasSession(Session* session) { + ASSERT(NULL != session_); + return (session_ == session); +} + +Session* TunnelSession::ReleaseSession(bool channel_exists) { + ASSERT(NULL != session_); + ASSERT(NULL != channel_); + Session* session = session_; + session_->SignalState.disconnect(this); + session_ = NULL; + if (channel_exists) + channel_->SignalChannelClosed.disconnect(this); + channel_ = NULL; + delete this; + return session; +} + +void TunnelSession::OnSessionState(Session* session, Session::State state) { + LOG(LS_INFO) << "TunnelSession::OnSessionState(" + << talk_base::nonnull( + talk_base::FindLabel(state, SESSION_STATES), "Unknown") + << ")"; + ASSERT(session == session_); + + switch (state) { + case Session::STATE_RECEIVEDINITIATE: + OnInitiate(); + break; + case Session::STATE_SENTACCEPT: + case Session::STATE_RECEIVEDACCEPT: + OnAccept(); + break; + case Session::STATE_SENTTERMINATE: + case Session::STATE_RECEIVEDTERMINATE: + OnTerminate(); + break; + case Session::STATE_DEINIT: + // ReleaseSession should have been called before this. + ASSERT(false); + break; + } +} + +void TunnelSession::OnInitiate() { + const TunnelSessionDescription* in_desc = + static_cast<const TunnelSessionDescription*>( + session_->remote_description()); + + ASSERT(client_ != NULL); + ASSERT(session_ != NULL); + client_->SignalIncomingTunnel(client_, + buzz::Jid(session_->remote_name()), + in_desc->description, + session_); +} + +void TunnelSession::OnAccept() { + ASSERT(channel_ != NULL); + VERIFY(channel_->Connect("tcp")); +} + +void TunnelSession::OnTerminate() { +} + +void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) { + ASSERT(channel_ == channel); + ASSERT(session_ != NULL); + session_->Terminate(); +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h new file mode 100644 index 0000000..34c05bf --- /dev/null +++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h @@ -0,0 +1,135 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef __TUNNELSESSIONCLIENT_H__ +#define __TUNNELSESSIONCLIENT_H__ + +#include <vector> +#include "talk/base/criticalsection.h" +#include "talk/base/stream.h" +#include "talk/p2p/base/pseudotcp.h" +#include "talk/p2p/base/session.h" +#include "talk/p2p/base/sessiondescription.h" +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/base/sessionclient.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/constants.h" + +namespace cricket { + +class TunnelSession; +class TunnelStream; + +/////////////////////////////////////////////////////////////////////////////// +// TunnelSessionClient +/////////////////////////////////////////////////////////////////////////////// + +class TunnelSessionClient + : public SessionClient, public talk_base::MessageHandler, + public sigslot::has_slots<> { +public: + TunnelSessionClient(const buzz::Jid& jid, SessionManager* manager); + virtual ~TunnelSessionClient(); + + const buzz::Jid& jid() const { return jid_; } + SessionManager* session_manager() const { return session_manager_; } + + const SessionDescription* CreateSessionDescription( + const buzz::XmlElement* element); + buzz::XmlElement* TranslateSessionDescription( + const SessionDescription* description); + + void OnSessionCreate(Session* session, bool received); + void OnSessionDestroy(Session* session); + + // This can be called on any thread. The stream interface is thread-safe, but + // notifications must be registered on the creating thread. + talk_base::StreamInterface* CreateTunnel(const buzz::Jid& to, + const std::string& description); + + // Signal arguments are this, initiator, description, session + sigslot::signal4<TunnelSessionClient*, buzz::Jid, std::string, Session*> + SignalIncomingTunnel; + talk_base::StreamInterface* AcceptTunnel(Session* session); + void DeclineTunnel(Session* session); + +private: + void OnMessage(talk_base::Message* pmsg); + + buzz::Jid jid_; + SessionManager* session_manager_; + std::vector<TunnelSession*> sessions_; + bool shutdown_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// TunnelSession +// Note: The lifetime of TunnelSession is complicated. It needs to survive +// until the following three conditions are true: +// 1) TunnelStream has called Close (tracked via non-null stream_) +// 2) PseudoTcp has completed (tracked via non-null tcp_) +// 3) Session has been destroyed (tracked via non-null session_) +// This is accomplished by calling CheckDestroy after these indicators change. +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// TunnelStream +// Note: Because TunnelStream provides a stream interface, it's lifetime is +// controlled by the owner of the stream pointer. As a result, we must support +// both the TunnelSession disappearing before TunnelStream, and vice versa. +/////////////////////////////////////////////////////////////////////////////// + +class PseudoTcpChannel; + +class TunnelSession : public sigslot::has_slots<> { +public: + // Signalling thread methods + TunnelSession(TunnelSessionClient* client, Session* session, + talk_base::Thread* stream_thread); + + talk_base::StreamInterface* GetStream(); + bool HasSession(Session* session); + Session* ReleaseSession(bool channel_exists); + +private: + virtual ~TunnelSession(); + + void OnSessionState(Session* session, Session::State state); + void OnInitiate(); + void OnAccept(); + void OnTerminate(); + void OnChannelClosed(PseudoTcpChannel* channel); + + TunnelSessionClient* client_; + Session* session_; + PseudoTcpChannel* channel_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace cricket + +#endif // __TUNNELSESSIONCLIENT_H__ diff --git a/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll Binary files differnew file mode 100644 index 0000000..a6fe7da --- /dev/null +++ b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.dll diff --git a/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib Binary files differnew file mode 100644 index 0000000..07aca57 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/third_party/dll/mediastreamer2.lib diff --git a/Plugins/jingle/libjingle/talk/xmllite/qname.cc b/Plugins/jingle/libjingle/talk/xmllite/qname.cc new file mode 100644 index 0000000..626cfa9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/qname.cc @@ -0,0 +1,167 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <string> +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlconstants.h" + +//#define new TRACK_NEW + +namespace buzz { + +static int QName_Hash(const std::string & ns, const char * local) { + int result = ns.size() * 101; + while (*local) { + result *= 19; + result += *local; + local += 1; + } + return result; +} + +static const int bits = 9; +static QName::Data * get_qname_table() { + static QName::Data qname_table[1 << bits]; + return qname_table; +} + +static QName::Data * +AllocateOrFind(const std::string & ns, const char * local) { + int index = QName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + QName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + return new QName::Data(ns, local); + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +static QName::Data * +Add(const std::string & ns, const char * local) { + int index = QName_Hash(ns, local); + int increment = index >> (bits - 1) | 1; + QName::Data * qname_table = get_qname_table(); + for (;;) { + index &= ((1 << bits) - 1); + if (!qname_table[index].Occupied()) { + qname_table[index].namespace_ = ns; + qname_table[index].localPart_ = local; + qname_table[index].AddRef(); // AddRef twice so it's never deleted + qname_table[index].AddRef(); + return qname_table + index; + } + if (qname_table[index].localPart_ == local && + qname_table[index].namespace_ == ns) { + qname_table[index].AddRef(); + return qname_table + index; + } + index += increment; + } +} + +QName::~QName() { + data_->Release(); +} + +QName::QName() : data_(QN_EMPTY.data_) { + data_->AddRef(); +} + +QName::QName(bool add, const std::string & ns, const char * local) : + data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {} + +QName::QName(bool add, const std::string & ns, const std::string & local) : + data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {} + +QName::QName(const std::string & ns, const char * local) : + data_(AllocateOrFind(ns, local)) {} + +static std::string +QName_LocalPart(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return name; + return name.substr(i + 1); +} + +static std::string +QName_Namespace(const std::string & name) { + size_t i = name.rfind(':'); + if (i == std::string::npos) + return STR_EMPTY; + return name.substr(0, i); +} + +QName::QName(const std::string & mergedOrLocal) : + data_(AllocateOrFind(QName_Namespace(mergedOrLocal), + QName_LocalPart(mergedOrLocal).c_str())) {} + +std::string +QName::Merged() const { + if (data_->namespace_ == STR_EMPTY) + return data_->localPart_; + + std::string result(data_->namespace_); + result.reserve(result.length() + 1 + data_->localPart_.length()); + result += ':'; + result += data_->localPart_; + return result; +} + +bool +QName::operator==(const QName & other) const { + return other.data_ == data_ || + data_->localPart_ == other.data_->localPart_ && + data_->namespace_ == other.data_->namespace_; +} + +int +QName::Compare(const QName & other) const { + if (data_ == other.data_) + return 0; + + int result = data_->localPart_.compare(other.data_->localPart_); + if (result) + return result; + + return data_->namespace_.compare(other.data_->namespace_); +} + +} + + + diff --git a/Plugins/jingle/libjingle/talk/xmllite/qname.h b/Plugins/jingle/libjingle/talk/xmllite/qname.h new file mode 100644 index 0000000..b1bcec6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/qname.h @@ -0,0 +1,87 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _qname_h_ +#define _qname_h_ + +#include <string> + +namespace buzz { + + +class QName +{ +public: + explicit QName(); + QName(const QName & qname) : data_(qname.data_) { data_->AddRef(); } + explicit QName(bool add, const std::string & ns, const char * local); + explicit QName(bool add, const std::string & ns, const std::string & local); + explicit QName(const std::string & ns, const char * local); + explicit QName(const std::string & mergedOrLocal); + QName & operator=(const QName & qn) { + qn.data_->AddRef(); + data_->Release(); + data_ = qn.data_; + return *this; + } + ~QName(); + + const std::string & Namespace() const { return data_->namespace_; } + const std::string & LocalPart() const { return data_->localPart_; } + std::string Merged() const; + int Compare(const QName & other) const; + bool operator==(const QName & other) const; + bool operator!=(const QName & other) const { return !operator==(other); } + bool operator<(const QName & other) const { return Compare(other) < 0; } + + class Data { + public: + Data(const std::string & ns, const std::string & local) : + refcount_(1), + namespace_(ns), + localPart_(local) {} + + Data() : refcount_(0) {} + + std::string namespace_; + std::string localPart_; + void AddRef() { refcount_++; } + void Release() { if (!--refcount_) { delete this; } } + bool Occupied() { return !!refcount_; } + + private: + int refcount_; + }; + +private: + Data * data_; +}; + + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc new file mode 100644 index 0000000..503f832 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.cc @@ -0,0 +1,65 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "xmlconstants.h" + +using namespace buzz; + +const std::string & XmlConstants::str_empty() { + static const std::string str_empty_; + return str_empty_; +} + +const std::string & XmlConstants::ns_xml() { + static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace"); + return ns_xml_; +} + +const std::string & XmlConstants::ns_xmlns() { + static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/"); + return ns_xmlns_; +} + +const std::string & XmlConstants::str_xmlns() { + static const std::string str_xmlns_("xmlns"); + return str_xmlns_; +} + +const std::string & XmlConstants::str_xml() { + static const std::string str_xml_("xml"); + return str_xml_; +} + +const std::string & XmlConstants::str_version() { + static const std::string str_version_("version"); + return str_version_; +} + +const std::string & XmlConstants::str_encoding() { + static const std::string str_encoding_("encoding"); + return str_encoding_; +} diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h new file mode 100644 index 0000000..8514d6f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlconstants.h @@ -0,0 +1,61 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// Because global constant initialization order is undefined +// globals cannot depend on other objects to be instantiated. +// This class creates string objects within static methods +// such that globals may refer to these constants by the +// accessor function and they are guaranteed to be initialized. + +#ifndef TALK_XMLLITE_CONSTANTS_H_ +#define TALK_XMLLITE_CONSTANTS_H_ + +#include <string> + +#define STR_EMPTY XmlConstants::str_empty() +#define NS_XML XmlConstants::ns_xml() +#define NS_XMLNS XmlConstants::ns_xmlns() +#define STR_XMLNS XmlConstants::str_xmlns() +#define STR_XML XmlConstants::str_xml() +#define STR_VERSION XmlConstants::str_version() +#define STR_ENCODING XmlConstants::str_encoding() +namespace buzz { + +class XmlConstants { + public: + static const std::string & str_empty(); + static const std::string & ns_xml(); + static const std::string & ns_xmlns(); + static const std::string & str_xmlns(); + static const std::string & str_xml(); + static const std::string & str_version(); + static const std::string & str_encoding(); +}; + +} + +#endif // TALK_XMLLITE_CONSTANTS_H_ diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc new file mode 100644 index 0000000..7326332 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.cc @@ -0,0 +1,501 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <string> +#include <iostream> +#include <vector> +#include <sstream> + +#include "talk/base/common.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +//#include "talk/xmllite/xmlparser.h" +//#include "talk/xmllite/xmlbuilder.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +const QName QN_EMPTY(true, STR_EMPTY, STR_EMPTY); +const QName QN_XMLNS(true, STR_EMPTY, STR_XMLNS); + + +XmlChild::~XmlChild() { +} + +bool +XmlText::IsTextImpl() const { + return true; +} + +XmlElement * +XmlText::AsElementImpl() const { + return NULL; +} + +XmlText * +XmlText::AsTextImpl() const { + return const_cast<XmlText *>(this); +} + +void +XmlText::SetText(const std::string & text) { + text_ = text; +} + +void +XmlText::AddParsedText(const char * buf, int len) { + text_.append(buf, len); +} + +void +XmlText::AddText(const std::string & text) { + text_ += text; +} + +XmlText::~XmlText() { +} + +XmlElement::XmlElement(const QName & name) : + name_(name), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +XmlElement::XmlElement(const XmlElement & elt) : + XmlChild(), + name_(elt.name_), + pFirstAttr_(NULL), + pLastAttr_(NULL), + pFirstChild_(NULL), + pLastChild_(NULL) { + + // copy attributes + XmlAttr * pAttr; + XmlAttr ** ppLastAttr = &pFirstAttr_; + XmlAttr * newAttr = NULL; + for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) { + newAttr = new XmlAttr(*pAttr); + *ppLastAttr = newAttr; + ppLastAttr = &(newAttr->pNextAttr_); + } + pLastAttr_ = newAttr; + + // copy children + XmlChild * pChild; + XmlChild ** ppLast = &pFirstChild_; + XmlChild * newChild = NULL; + + for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) { + if (pChild->IsText()) { + newChild = new XmlText(*(pChild->AsText())); + } else { + newChild = new XmlElement(*(pChild->AsElement())); + } + *ppLast = newChild; + ppLast = &(newChild->pNextChild_); + } + pLastChild_ = newChild; + +} + +XmlElement::XmlElement(const QName & name, bool useDefaultNs) : + name_(name), + pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL), + pLastAttr_(pFirstAttr_), + pFirstChild_(NULL), + pLastChild_(NULL) { +} + +bool +XmlElement::IsTextImpl() const { + return false; +} + +XmlElement * +XmlElement::AsElementImpl() const { + return const_cast<XmlElement *>(this); +} + +XmlText * +XmlElement::AsTextImpl() const { + return NULL; +} + +const std::string & +XmlElement::BodyText() const { + if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + return pFirstChild_->AsText()->Text(); + } + + return STR_EMPTY; +} + +void +XmlElement::SetBodyText(const std::string & text) { + if (text == STR_EMPTY) { + ClearChildren(); + } else if (pFirstChild_ == NULL) { + AddText(text); + } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { + pFirstChild_->AsText()->SetText(text); + } else { + ClearChildren(); + AddText(text); + } +} + +const QName & +XmlElement::FirstElementName() const { + const XmlElement * element = FirstElement(); + if (element == NULL) + return QN_EMPTY; + return element->Name(); +} + +XmlAttr * +XmlElement::FirstAttr() { + return pFirstAttr_; +} + +const std::string & +XmlElement::Attr(const QName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return pattr->value_; + } + return STR_EMPTY; +} + +bool +XmlElement::HasAttr(const QName & name) const { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + return true; + } + return false; +} + +void +XmlElement::SetAttr(const QName & name, const std::string & value) { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + } + if (!pattr) { + pattr = new XmlAttr(name, value); + if (pLastAttr_) + pLastAttr_->pNextAttr_ = pattr; + else + pFirstAttr_ = pattr; + pLastAttr_ = pattr; + return; + } + pattr->value_ = value; +} + +void +XmlElement::ClearAttr(const QName & name) { + XmlAttr * pattr; + XmlAttr *pLastAttr = NULL; + for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { + if (pattr->name_ == name) + break; + pLastAttr = pattr; + } + if (!pattr) + return; + if (!pLastAttr) + pFirstAttr_ = pattr->pNextAttr_; + else + pLastAttr->pNextAttr_ = pattr->pNextAttr_; + if (pLastAttr_ == pattr) + pLastAttr_ = pLastAttr; + delete pattr; +} + +XmlChild * +XmlElement::FirstChild() { + return pFirstChild_; +} + +XmlElement * +XmlElement::FirstElement() { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextElement() { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText()) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextWithNamespace(const std::string & ns) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::FirstNamed(const QName & name) { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement * +XmlElement::NextNamed(const QName & name) { + XmlChild * pChild; + for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement(); + } + return NULL; +} + +XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) { + XmlElement* child = FirstNamed(name); + if (!child) { + child = new XmlElement(name); + AddElement(child); + } + + return child; +} + +const std::string & +XmlElement::TextNamed(const QName & name) const { + XmlChild * pChild; + for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { + if (!pChild->IsText() && pChild->AsElement()->Name() == name) + return pChild->AsElement()->BodyText(); + } + return STR_EMPTY; +} + +void +XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) { + if (pPredecessor == NULL) { + pNext->pNextChild_ = pFirstChild_; + pFirstChild_ = pNext; + } + else { + pNext->pNextChild_ = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext; + } +} + +void +XmlElement::RemoveChildAfter(XmlChild * pPredecessor) { + XmlChild * pNext; + + if (pPredecessor == NULL) { + pNext = pFirstChild_; + pFirstChild_ = pNext->pNextChild_; + } + else { + pNext = pPredecessor->pNextChild_; + pPredecessor->pNextChild_ = pNext->pNextChild_; + } + + if (pLastChild_ == pNext) + pLastChild_ = pPredecessor; + + delete pNext; +} + +void +XmlElement::AddAttr(const QName & name, const std::string & value) { + ASSERT(!HasAttr(name)); + + XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_; + pLastAttr_ = (*pprev = new XmlAttr(name, value)); +} + +void +XmlElement::AddAttr(const QName & name, const std::string & value, + int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddAttr(name, value); +} + +void +XmlElement::AddParsedText(const char * cstr, int len) { + if (len == 0) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddParsedText(cstr, len); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(cstr, len); +} + +void +XmlElement::AddText(const std::string & text) { + if (text == STR_EMPTY) + return; + + if (pLastChild_ && pLastChild_->IsText()) { + pLastChild_->AsText()->AddText(text); + return; + } + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = new XmlText(text); +} + +void +XmlElement::AddText(const std::string & text, int depth) { + // note: the first syntax is ambigious for msvc 6 + // XmlElement * pel(this); + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddText(text); +} + +void +XmlElement::AddElement(XmlElement *pelChild) { + if (pelChild == NULL) + return; + + XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; + pLastChild_ = *pprev = pelChild; + pelChild->pNextChild_ = NULL; +} + +void +XmlElement::AddElement(XmlElement *pelChild, int depth) { + XmlElement * element = this; + while (depth--) { + element = element->pLastChild_->AsElement(); + } + element->AddElement(pelChild); +} + +void +XmlElement::ClearNamedChildren(const QName & name) { + XmlChild * prev_child = NULL; + XmlChild * next_child; + XmlChild * child; + for (child = FirstChild(); child; child = next_child) { + next_child = child->NextChild(); + if (!child->IsText() && child->AsElement()->Name() == name) + { + RemoveChildAfter(prev_child); + continue; + } + prev_child = child; + } +} + +void +XmlElement::ClearChildren() { + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } + pFirstChild_ = pLastChild_ = NULL; +} + +std::string +XmlElement::Str() const { + std::stringstream ss; + Print(&ss, NULL, 0); + return ss.str(); +} + +/* +XmlElement * +XmlElement::ForStr(const std::string & str) { + XmlBuilder builder; + XmlParser::ParseXml(&builder, str); + return builder.CreateElement(); +} +*/ + +void +XmlElement::Print( + std::ostream * pout, std::string xmlns[], int xmlnsCount) const { + XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount); +} + +XmlElement::~XmlElement() { + XmlAttr * pattr; + for (pattr = pFirstAttr_; pattr; ) { + XmlAttr * pToDelete = pattr; + pattr = pattr->pNextAttr_; + delete pToDelete; + } + + XmlChild * pchild; + for (pchild = pFirstChild_; pchild; ) { + XmlChild * pToDelete = pchild; + pchild = pchild->pNextChild_; + delete pToDelete; + } +} + +} diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h new file mode 100644 index 0000000..21c10a6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlelement.h @@ -0,0 +1,232 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _xmlelement_h_ +#define _xmlelement_h_ + +#include <iosfwd> +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/xmllite/qname.h" + +namespace buzz { + +extern const QName QN_EMPTY; +extern const QName QN_XMLNS; + + +class XmlChild; +class XmlText; +class XmlElement; +class XmlAttr; + +class XmlChild { +friend class XmlElement; + +public: + + XmlChild * NextChild() { return pNextChild_; } + const XmlChild * NextChild() const { return pNextChild_; } + + bool IsText() const { return IsTextImpl(); } + + XmlElement * AsElement() { return AsElementImpl(); } + const XmlElement * AsElement() const { return AsElementImpl(); } + + XmlText * AsText() { return AsTextImpl(); } + const XmlText * AsText() const { return AsTextImpl(); } + + +protected: + + XmlChild() : + pNextChild_(NULL) { + } + + virtual bool IsTextImpl() const = 0; + virtual XmlElement * AsElementImpl() const = 0; + virtual XmlText * AsTextImpl() const = 0; + + + virtual ~XmlChild(); + +private: + XmlChild(const XmlChild & noimpl); + + XmlChild * pNextChild_; + +}; + +class XmlText : public XmlChild { +public: + explicit XmlText(const std::string & text) : + XmlChild(), + text_(text) { + } + explicit XmlText(const XmlText & t) : + XmlChild(), + text_(t.text_) { + } + explicit XmlText(const char * cstr, size_t len) : + XmlChild(), + text_(cstr, len) { + } + virtual ~XmlText(); + + const std::string & Text() const { return text_; } + void SetText(const std::string & text); + void AddParsedText(const char * buf, int len); + void AddText(const std::string & text); + +protected: + virtual bool IsTextImpl() const; + virtual XmlElement * AsElementImpl() const; + virtual XmlText * AsTextImpl() const; + +private: + std::string text_; +}; + +class XmlAttr { +friend class XmlElement; + +public: + XmlAttr * NextAttr() const { return pNextAttr_; } + const QName & Name() const { return name_; } + const std::string & Value() const { return value_; } + +private: + explicit XmlAttr(const QName & name, const std::string & value) : + pNextAttr_(NULL), + name_(name), + value_(value) { + } + explicit XmlAttr(const XmlAttr & att) : + pNextAttr_(NULL), + name_(att.name_), + value_(att.value_) { + } + + XmlAttr * pNextAttr_; + QName name_; + std::string value_; +}; + +class XmlElement : public XmlChild { +public: + explicit XmlElement(const QName & name); + explicit XmlElement(const QName & name, bool useDefaultNs); + explicit XmlElement(const XmlElement & elt); + + virtual ~XmlElement(); + + const QName& Name() const { return name_; } + void SetName(const QName& name) { name_ = name; } + + const std::string & BodyText() const; + void SetBodyText(const std::string & text); + + const QName & FirstElementName() const; + + XmlAttr * FirstAttr(); + const XmlAttr * FirstAttr() const + { return const_cast<XmlElement *>(this)->FirstAttr(); } + + //! Attr will return STR_EMPTY if the attribute isn't there: + //! use HasAttr to test presence of an attribute. + const std::string & Attr(const QName & name) const; + bool HasAttr(const QName & name) const; + void SetAttr(const QName & name, const std::string & value); + void ClearAttr(const QName & name); + + XmlChild * FirstChild(); + const XmlChild * FirstChild() const + { return const_cast<XmlElement *>(this)->FirstChild(); } + + XmlElement * FirstElement(); + const XmlElement * FirstElement() const + { return const_cast<XmlElement *>(this)->FirstElement(); } + + XmlElement * NextElement(); + const XmlElement * NextElement() const + { return const_cast<XmlElement *>(this)->NextElement(); } + + XmlElement * FirstWithNamespace(const std::string & ns); + const XmlElement * FirstWithNamespace(const std::string & ns) const + { return const_cast<XmlElement *>(this)->FirstWithNamespace(ns); } + + XmlElement * NextWithNamespace(const std::string & ns); + const XmlElement * NextWithNamespace(const std::string & ns) const + { return const_cast<XmlElement *>(this)->NextWithNamespace(ns); } + + XmlElement * FirstNamed(const QName & name); + const XmlElement * FirstNamed(const QName & name) const + { return const_cast<XmlElement *>(this)->FirstNamed(name); } + + XmlElement * NextNamed(const QName & name); + const XmlElement * NextNamed(const QName & name) const + { return const_cast<XmlElement *>(this)->NextNamed(name); } + + // Finds the first element named 'name'. If that element can't be found then + // adds one and returns it. + XmlElement* FindOrAddNamedChild(const QName& name); + + const std::string & TextNamed(const QName & name) const; + + void InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNewChild); + void RemoveChildAfter(XmlChild * pPredecessor); + + void AddParsedText(const char * buf, int len); + void AddText(const std::string & text); + void AddText(const std::string & text, int depth); + void AddElement(XmlElement * pelChild); + void AddElement(XmlElement * pelChild, int depth); + void AddAttr(const QName & name, const std::string & value); + void AddAttr(const QName & name, const std::string & value, int depth); + void ClearNamedChildren(const QName & name); + void ClearChildren(); + +// static XmlElement * ForStr(const std::string & str); + std::string Str() const; + + void Print(std::ostream * pout, std::string xmlns[], int xmlnsCount) const; + +protected: + virtual bool IsTextImpl() const; + virtual XmlElement * AsElementImpl() const; + virtual XmlText * AsTextImpl() const; + +private: + QName name_; + XmlAttr * pFirstAttr_; + XmlAttr * pLastAttr_; + XmlChild * pFirstChild_; + XmlChild * pLastChild_; +}; + +} +#endif diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc new file mode 100644 index 0000000..4dcb649 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.cc @@ -0,0 +1,205 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/stl_decl.h" +#include <string> +#include <iostream> +#include <vector> +#include <sstream> +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +XmlnsStack::XmlnsStack() : + pxmlnsStack_(new std::vector<std::string>), + pxmlnsDepthStack_(new std::vector<size_t>) { +} + +XmlnsStack::~XmlnsStack() {} + +void +XmlnsStack::PushFrame() { + pxmlnsDepthStack_->push_back(pxmlnsStack_->size()); +} + +void +XmlnsStack::PopFrame() { + size_t prev_size = pxmlnsDepthStack_->back(); + pxmlnsDepthStack_->pop_back(); + if (prev_size < pxmlnsStack_->size()) { + pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size, + pxmlnsStack_->end()); + } +} +const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false); +const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true); +const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true); + +const std::string * +XmlnsStack::NsForPrefix(const std::string & prefix) { + if (prefix.length() >= 3 && + (prefix[0] == 'x' || prefix[0] == 'X') && + (prefix[1] == 'm' || prefix[1] == 'M') && + (prefix[2] == 'l' || prefix[2] == 'L')) { + if (prefix == "xml") + return &(NS_XML); + if (prefix == "xmlns") + return &(NS_XMLNS); + return NULL; + } + + std::vector<std::string>::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*pos == prefix) + return &(*(pos + 1)); + } + + if (prefix == STR_EMPTY) + return &(STR_EMPTY); // default namespace + + return NULL; // none found +} + +bool +XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) { + const std::string * match = NsForPrefix(prefix); + if (match == NULL) + return false; + return (*match == ns); +} + +std::pair<std::string, bool> +XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) { + if (ns == NS_XML) + return std::make_pair(std::string("xml"), true); + if (ns == NS_XMLNS) + return std::make_pair(std::string("xmlns"), true); + if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns)) + return std::make_pair(STR_EMPTY, true); + + std::vector<std::string>::iterator pos; + for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) { + pos -= 2; + if (*(pos + 1) == ns && + (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns)) + return std::make_pair(*pos, true); + } + + return std::make_pair(STR_EMPTY, false); // none found +} + +std::string +XmlnsStack::FormatQName(const QName & name, bool isAttr) { + std::string prefix(PrefixForNs(name.Namespace(), isAttr).first); + if (prefix == STR_EMPTY) + return name.LocalPart(); + else + return prefix + ':' + name.LocalPart(); +} + +void +XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) { + pxmlnsStack_->push_back(prefix); + pxmlnsStack_->push_back(ns); +} + +void +XmlnsStack::RemoveXmlns() { + pxmlnsStack_->pop_back(); + pxmlnsStack_->pop_back(); +} + +static bool IsAsciiLetter(char ch) { + return ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z')); +} + +static std::string AsciiLower(const std::string & s) { + std::string result(s); + size_t i; + for (i = 0; i < result.length(); i++) { + if (result[i] >= 'A' && result[i] <= 'Z') + result[i] += 'a' - 'A'; + } + return result; +} + +static std::string SuggestPrefix(const std::string & ns) { + size_t len = ns.length(); + size_t i = ns.find_last_of('.'); + if (i != std::string::npos && len - i <= 4 + 1) + len = i; // chop off ".html" or ".xsd" or ".?{0,4}" + size_t last = len; + while (last > 0) { + last -= 1; + if (IsAsciiLetter(ns[last])) { + size_t first = last; + last += 1; + while (first > 0) { + if (!IsAsciiLetter(ns[first - 1])) + break; + first -= 1; + } + if (last - first > 4) + last = first + 3; + std::string candidate(AsciiLower(ns.substr(first, last - first))); + if (candidate.find("xml") != 0) + return candidate; + break; + } + } + return "ns"; +} + + +std::pair<std::string, bool> +XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) { + if (PrefixForNs(ns, isAttr).second) + return std::make_pair(STR_EMPTY, false); + + std::string base(SuggestPrefix(ns)); + std::string result(base); + int i = 2; + while (NsForPrefix(result) != NULL) { + std::stringstream ss; + ss << base; + ss << (i++); + ss >> result; + } + AddXmlns(result, ns); + return std::make_pair(result, true); +} + +void XmlnsStack::Reset() { + pxmlnsStack_->clear(); + pxmlnsDepthStack_->clear(); +} + +} diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h new file mode 100644 index 0000000..299ec1c --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlnsstack.h @@ -0,0 +1,62 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _xmlnsstack_h_ +#define _xmlnsstack_h_ + +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/base/stl_decl.h" +#include "talk/xmllite/qname.h" + +namespace buzz { + +class XmlnsStack { +public: + XmlnsStack(); + ~XmlnsStack(); + + void AddXmlns(const std::string & prefix, const std::string & ns); + void RemoveXmlns(); + void PushFrame(); + void PopFrame(); + void Reset(); + + const std::string * NsForPrefix(const std::string & prefix); + bool PrefixMatchesNs(const std::string & prefix, const std::string & ns); + std::pair<std::string, bool> PrefixForNs(const std::string & ns, bool isAttr); + std::pair<std::string, bool> AddNewPrefix(const std::string & ns, bool isAttr); + std::string FormatQName(const QName & name, bool isAttr); + +private: + + scoped_ptr<std::vector<std::string, std::allocator<std::string> > > pxmlnsStack_; + scoped_ptr<std::vector<size_t, std::allocator<size_t> > > pxmlnsDepthStack_; +}; +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc new file mode 100644 index 0000000..86f143a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.cc @@ -0,0 +1,190 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/stl_decl.h" +#include <string> +#include <iostream> +#include <vector> +#include <sstream> +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlprinter.h" +#include "talk/xmllite/xmlnsstack.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +class XmlPrinterImpl { +public: + XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount); + void PrintElement(const XmlElement * element); + void PrintQuotedValue(const std::string & text); + void PrintBodyText(const std::string & text); + +private: + std::ostream *pout_; + XmlnsStack xmlnsStack_; +}; + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) { + PrintXml(pout, element, NULL, 0); +} + +void +XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element, + const std::string * const xmlns, int xmlnsCount) { + XmlPrinterImpl printer(pout, xmlns, xmlnsCount); + printer.PrintElement(element); +} + +XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout, + const std::string * const xmlns, int xmlnsCount) : + pout_(pout), + xmlnsStack_() { + int i; + for (i = 0; i < xmlnsCount; i += 2) { + xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]); + } +} + +void +XmlPrinterImpl::PrintElement(const XmlElement * element) { + xmlnsStack_.PushFrame(); + + // first go through attrs of pel to add xmlns definitions + const XmlAttr * pattr; + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + if (pattr->Name() == QN_XMLNS) + xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value()); + else if (pattr->Name().Namespace() == NS_XMLNS) + xmlnsStack_.AddXmlns(pattr->Name().LocalPart(), + pattr->Value()); + } + + // then go through qnames to make sure needed xmlns definitons are added + std::vector<std::string> newXmlns; + std::pair<std::string, bool> prefix; + prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(element->Name().Namespace()); + } + + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true); + if (prefix.second) { + newXmlns.push_back(prefix.first); + newXmlns.push_back(pattr->Name().Namespace()); + } + } + + // print the element name + *pout_ << '<' << xmlnsStack_.FormatQName(element->Name(), false); + + // and the attributes + for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) { + *pout_ << ' ' << xmlnsStack_.FormatQName(pattr->Name(), true) << "=\""; + PrintQuotedValue(pattr->Value()); + *pout_ << '"'; + } + + // and the extra xmlns declarations + std::vector<std::string>::iterator i(newXmlns.begin()); + while (i < newXmlns.end()) { + if (*i == STR_EMPTY) + *pout_ << " xmlns=\"" << *(i + 1) << '"'; + else + *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"'; + i += 2; + } + + // now the children + const XmlChild * pchild = element->FirstChild(); + + if (pchild == NULL) + *pout_ << "/>"; + else { + *pout_ << '>'; + while (pchild) { + if (pchild->IsText()) + PrintBodyText(pchild->AsText()->Text()); + else + PrintElement(pchild->AsElement()); + pchild = pchild->NextChild(); + } + *pout_ << "</" << xmlnsStack_.FormatQName(element->Name(), false) << '>'; + } + + xmlnsStack_.PopFrame(); +} + +void +XmlPrinterImpl::PrintQuotedValue(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&\"", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + case '"': *pout_ << """; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + +void +XmlPrinterImpl::PrintBodyText(const std::string & text) { + size_t safe = 0; + for (;;) { + size_t unsafe = text.find_first_of("<>&", safe); + if (unsafe == std::string::npos) + unsafe = text.length(); + *pout_ << text.substr(safe, unsafe - safe); + if (unsafe == text.length()) + return; + switch (text[unsafe]) { + case '<': *pout_ << "<"; break; + case '>': *pout_ << ">"; break; + case '&': *pout_ << "&"; break; + } + safe = unsafe + 1; + if (safe == text.length()) + return; + } +} + + +} diff --git a/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h new file mode 100644 index 0000000..96900d0 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmllite/xmlprinter.h @@ -0,0 +1,49 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _xmlprinter_h_ +#define _xmlprinter_h_ + +#include <iosfwd> +#include <string> +#include "talk/base/scoped_ptr.h" + +namespace buzz { + +class XmlElement; + +class XmlPrinter { +public: + static void PrintXml(std::ostream * pout, const XmlElement * pelt); + + static void PrintXml(std::ostream * pout, const XmlElement * pelt, + const std::string * const xmlns, int xmlnsCount); +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h b/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h new file mode 100644 index 0000000..e4bce7f --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/asyncsocket.h @@ -0,0 +1,86 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _ASYNCSOCKET_H_ +#define _ASYNCSOCKET_H_ + +#include "talk/base/sigslot.h" + +namespace talk_base { + class SocketAddress; +} + +namespace buzz { + +class AsyncSocket { +public: + enum State { + STATE_CLOSED = 0, //!< Socket is not open. + STATE_CLOSING, //!< Socket is closing but can have buffered data + STATE_CONNECTING, //!< In the process of + STATE_OPEN, //!< Socket is connected +#if defined(FEATURE_ENABLE_SSL) + STATE_TLS_CONNECTING, //!< Establishing TLS connection + STATE_TLS_OPEN, //!< TLS connected +#endif + }; + + enum Error { + ERROR_NONE = 0, //!< No error + ERROR_WINSOCK, //!< Winsock error + ERROR_DNS, //!< Couldn't resolve host name + ERROR_WRONGSTATE, //!< Call made while socket is in the wrong state +#if defined(FEATURE_ENABLE_SSL) + ERROR_SSL, //!< Something went wrong with OpenSSL +#endif + }; + + virtual ~AsyncSocket() {} + virtual State state() = 0; + virtual Error error() = 0; + virtual int GetError() = 0; // winsock error code + + virtual bool Connect(const talk_base::SocketAddress& addr) = 0; + virtual bool Read(char * data, size_t len, size_t* len_read) = 0; + virtual bool Write(const char * data, size_t len) = 0; + virtual bool Close() = 0; +#if defined(FEATURE_ENABLE_SSL) + // We allow matching any passed domain. + // If both names are passed as empty, we do not require a match. + virtual bool StartTls(const std::string & domainname) = 0; +#endif + + sigslot::signal0<> SignalConnected; + sigslot::signal0<> SignalSSLConnected; + sigslot::signal0<> SignalClosed; + sigslot::signal0<> SignalRead; + sigslot::signal0<> SignalError; +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/constants.cc b/Plugins/jingle/libjingle/talk/xmpp/constants.cc new file mode 100644 index 0000000..9388aae --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/constants.cc @@ -0,0 +1,398 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <string> +#include "talk/base/basicdefs.h" +#include "talk/xmllite/xmlconstants.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/qname.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +namespace buzz { + +const Jid JID_EMPTY(STR_EMPTY); + +const std::string & Constants::ns_client() { + static const std::string ns_client_("jabber:client"); + return ns_client_; +} + +const std::string & Constants::ns_server() { + static const std::string ns_server_("jabber:server"); + return ns_server_; +} + +const std::string & Constants::ns_stream() { + static const std::string ns_stream_("http://etherx.jabber.org/streams"); + return ns_stream_; +} + +const std::string & Constants::ns_xstream() { + static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams"); + return ns_xstream_; +} + +const std::string & Constants::ns_tls() { + static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls"); + return ns_tls_; +} + +const std::string & Constants::ns_sasl() { + static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl"); + return ns_sasl_; +} + +const std::string & Constants::ns_bind() { + static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind"); + return ns_bind_; +} + +const std::string & Constants::ns_dialback() { + static const std::string ns_dialback_("jabber:server:dialback"); + return ns_dialback_; +} + +const std::string & Constants::ns_session() { + static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session"); + return ns_session_; +} + +const std::string & Constants::ns_stanza() { + static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas"); + return ns_stanza_; +} + +const std::string & Constants::ns_privacy() { + static const std::string ns_privacy_("jabber:iq:privacy"); + return ns_privacy_; +} + +const std::string & Constants::ns_roster() { + static const std::string ns_roster_("jabber:iq:roster"); + return ns_roster_; +} + +const std::string & Constants::ns_vcard() { + static const std::string ns_vcard_("vcard-temp"); + return ns_vcard_; +} + +const std::string & Constants::ns_avatar_hash() { + static const std::string ns_avatar_hash_("google:avatar"); + return ns_avatar_hash_; +} + +const std::string & Constants::ns_vcard_update() { + static const std::string ns_vcard_update_("vcard-temp:x:update"); + return ns_vcard_update_; +} + +const std::string & Constants::str_client() { + static const std::string str_client_("client"); + return str_client_; +} + +const std::string & Constants::str_server() { + static const std::string str_server_("server"); + return str_server_; +} + +const std::string & Constants::str_stream() { + static const std::string str_stream_("stream"); + return str_stream_; +} + +const std::string STR_GET("get"); +const std::string STR_SET("set"); +const std::string STR_RESULT("result"); +const std::string STR_ERROR("error"); + + +const std::string STR_FROM("from"); +const std::string STR_TO("to"); +const std::string STR_BOTH("both"); +const std::string STR_REMOVE("remove"); + +const std::string STR_UNAVAILABLE("unavailable"); + +const std::string STR_GOOGLE_COM("google.com"); +const std::string STR_GMAIL_COM("gmail.com"); +const std::string STR_GOOGLEMAIL_COM("googlemail.com"); +const std::string STR_DEFAULT_DOMAIN("default.talk.google.com"); +const std::string STR_TALK_GOOGLE_COM("talk.google.com"); +const std::string STR_TALKX_L_GOOGLE_COM("talkx.l.google.com"); + +const std::string STR_X("x"); + +#ifdef FEATURE_ENABLE_VOICEMAIL +const std::string STR_VOICEMAIL("voicemail"); +const std::string STR_OUTGOINGVOICEMAIL("outgoingvoicemail"); +#endif + +const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM); +const QName QN_STREAM_FEATURES(true, NS_STREAM, "features"); +const QName QN_STREAM_ERROR(true, NS_STREAM, "error"); + +const QName QN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format"); +const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix"); +const QName QN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict"); +const QName QN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout"); +const QName QN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone"); +const QName QN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown"); +const QName QN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing"); +const QName QN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error"); +const QName QN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from"); +const QName QN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id"); +const QName QN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace"); +const QName QN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml"); +const QName QN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized"); +const QName QN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation"); +const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed"); +const QName QN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint"); +const QName QN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml"); +const QName QN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host"); +const QName QN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown"); +const QName QN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition"); +const QName QN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding"); +const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type"); +const QName QN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version"); +const QName QN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed"); +const QName QN_XSTREAM_TEXT(true, NS_XSTREAM, "text"); + +const QName QN_TLS_STARTTLS(true, NS_TLS, "starttls"); +const QName QN_TLS_REQUIRED(true, NS_TLS, "required"); +const QName QN_TLS_PROCEED(true, NS_TLS, "proceed"); +const QName QN_TLS_FAILURE(true, NS_TLS, "failure"); + +const QName QN_SASL_MECHANISMS(true, NS_SASL, "mechanisms"); +const QName QN_SASL_MECHANISM(true, NS_SASL, "mechanism"); +const QName QN_SASL_AUTH(true, NS_SASL, "auth"); +const QName QN_SASL_CHALLENGE(true, NS_SASL, "challenge"); +const QName QN_SASL_RESPONSE(true, NS_SASL, "response"); +const QName QN_SASL_ABORT(true, NS_SASL, "abort"); +const QName QN_SASL_SUCCESS(true, NS_SASL, "success"); +const QName QN_SASL_FAILURE(true, NS_SASL, "failure"); +const QName QN_SASL_ABORTED(true, NS_SASL, "aborted"); +const QName QN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding"); +const QName QN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid"); +const QName QN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism"); +const QName QN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak"); +const QName QN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized"); +const QName QN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure"); + +const std::string NS_GOOGLE_AUTH("google:auth"); +const QName QN_MISSING_USERNAME(true, NS_GOOGLE_AUTH, "missing-username"); + +const QName QN_DIALBACK_RESULT(true, NS_DIALBACK, "result"); +const QName QN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify"); + +const QName QN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request"); +const QName QN_STANZA_CONFLICT(true, NS_STANZA, "conflict"); +const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented"); +const QName QN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden"); +const QName QN_STANZA_GONE(true, NS_STANZA, "gone"); +const QName QN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error"); +const QName QN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found"); +const QName QN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed"); +const QName QN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable"); +const QName QN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed"); +const QName QN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required"); +const QName QN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable"); +const QName QN_STANZA_REDIRECT(true, NS_STANZA, "redirect"); +const QName QN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required"); +const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found"); +const QName QN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout"); +const QName QN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint"); +const QName QN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable"); +const QName QN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required"); +const QName QN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition"); +const QName QN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request"); +const QName QN_STANZA_TEXT(true, NS_STANZA, "text"); + +const QName QN_BIND_BIND(true, NS_BIND, "bind"); +const QName QN_BIND_RESOURCE(true, NS_BIND, "resource"); +const QName QN_BIND_JID(true, NS_BIND, "jid"); + +const QName QN_MESSAGE(true, NS_CLIENT, "message"); +const QName QN_BODY(true, NS_CLIENT, "body"); +const QName QN_SUBJECT(true, NS_CLIENT, "subject"); +const QName QN_THREAD(true, NS_CLIENT, "thread"); +const QName QN_PRESENCE(true, NS_CLIENT, "presence"); +const QName QN_SHOW(true, NS_CLIENT, "show"); +const QName QN_STATUS(true, NS_CLIENT, "status"); +const QName QN_LANG(true, NS_CLIENT, "lang"); +const QName QN_PRIORITY(true, NS_CLIENT, "priority"); +const QName QN_IQ(true, NS_CLIENT, "iq"); +const QName QN_ERROR(true, NS_CLIENT, "error"); + +const QName QN_SERVER_MESSAGE(true, NS_SERVER, "message"); +const QName QN_SERVER_BODY(true, NS_SERVER, "body"); +const QName QN_SERVER_SUBJECT(true, NS_SERVER, "subject"); +const QName QN_SERVER_THREAD(true, NS_SERVER, "thread"); +const QName QN_SERVER_PRESENCE(true, NS_SERVER, "presence"); +const QName QN_SERVER_SHOW(true, NS_SERVER, "show"); +const QName QN_SERVER_STATUS(true, NS_SERVER, "status"); +const QName QN_SERVER_LANG(true, NS_SERVER, "lang"); +const QName QN_SERVER_PRIORITY(true, NS_SERVER, "priority"); +const QName QN_SERVER_IQ(true, NS_SERVER, "iq"); +const QName QN_SERVER_ERROR(true, NS_SERVER, "error"); + +const QName QN_SESSION_SESSION(true, NS_SESSION, "session"); + +const QName QN_PRIVACY_QUERY(true, NS_PRIVACY, "query"); +const QName QN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active"); +const QName QN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default"); +const QName QN_PRIVACY_LIST(true, NS_PRIVACY, "list"); +const QName QN_PRIVACY_ITEM(true, NS_PRIVACY, "item"); +const QName QN_PRIVACY_IQ(true, NS_PRIVACY, "iq"); +const QName QN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message"); +const QName QN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in"); +const QName QN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out"); + +const QName QN_ROSTER_QUERY(true, NS_ROSTER, "query"); +const QName QN_ROSTER_ITEM(true, NS_ROSTER, "item"); +const QName QN_ROSTER_GROUP(true, NS_ROSTER, "group"); + +const QName QN_VCARD(true, NS_VCARD, "vCard"); +const QName QN_VCARD_FN(true, NS_VCARD, "FN"); +const QName QN_VCARD_PHOTO(true, NS_VCARD, "PHOTO"); +const QName QN_VCARD_PHOTO_BINVAL(true, NS_VCARD, "BINVAL"); +const QName QN_VCARD_AVATAR_HASH(true, NS_AVATAR_HASH, "hash"); +const QName QN_VCARD_AVATAR_HASH_MODIFIED(true, NS_AVATAR_HASH, "modified"); + +const buzz::QName QN_NAME(true, STR_EMPTY, "name"); +const QName QN_XML_LANG(true, NS_XML, "lang"); + +const std::string STR_TYPE("type"); +const std::string STR_ID("id"); +const std::string STR_NAME("name"); +const std::string STR_JID("jid"); +const std::string STR_SUBSCRIPTION("subscription"); +const std::string STR_ASK("ask"); + +const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING); +const QName QN_VERSION(true, STR_EMPTY, STR_VERSION); +const QName QN_TO(true, STR_EMPTY, "to"); +const QName QN_FROM(true, STR_EMPTY, "from"); +const QName QN_TYPE(true, STR_EMPTY, "type"); +const QName QN_ID(true, STR_EMPTY, "id"); +const QName QN_CODE(true, STR_EMPTY, "code"); + +const QName QN_VALUE(true, STR_EMPTY, "value"); +const QName QN_ACTION(true, STR_EMPTY, "action"); +const QName QN_ORDER(true, STR_EMPTY, "order"); +const QName QN_MECHANISM(true, STR_EMPTY, "mechanism"); +const QName QN_ASK(true, STR_EMPTY, "ask"); +const QName QN_JID(true, STR_EMPTY, "jid"); +const QName QN_SUBSCRIPTION(true, STR_EMPTY, "subscription"); +const QName QN_TITLE1(true, STR_EMPTY, "title1"); +const QName QN_TITLE2(true, STR_EMPTY, "title2"); +const QName QN_SOURCE(true, STR_EMPTY, "source"); + +const QName QN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT); +const QName QN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER); +const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM); + + + +// Presence +const std::string STR_SHOW_AWAY("away"); +const std::string STR_SHOW_CHAT("chat"); +const std::string STR_SHOW_DND("dnd"); +const std::string STR_SHOW_XA("xa"); +const std::string STR_SHOW_OFFLINE("offline"); + +// Subscription +const std::string STR_SUBSCRIBE("subscribe"); +const std::string STR_SUBSCRIBED("subscribed"); +const std::string STR_UNSUBSCRIBE("unsubscribe"); +const std::string STR_UNSUBSCRIBED("unsubscribed"); + + +// JEP 0030 +const QName QN_NODE(true, STR_EMPTY, "node"); +const QName QN_CATEGORY(true, STR_EMPTY, "category"); +const QName QN_VAR(true, STR_EMPTY, "var"); +const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info"); +const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items"); +const QName QN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query"); +const QName QN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity"); +const QName QN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature"); + +const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query"); +const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item"); + + +// JEP 0115 +const std::string NS_CAPS("http://jabber.org/protocol/caps"); +const QName QN_CAPS_C(true, NS_CAPS, "c"); +const QName QN_VER(true, STR_EMPTY, "ver"); +const QName QN_EXT(true, STR_EMPTY, "ext"); + +// JEP 0153 +const std::string kNSVCard("vcard-temp:x:update"); +const QName kQnVCardX(true, kNSVCard, "x"); +const QName kQnVCardPhoto(true, kNSVCard, "photo"); + +// JEP 0172 User Nickname +const std::string kNSNickname("http://jabber.org/protocol/nick"); +const QName kQnNickname(true, kNSNickname, "nick"); + + +// JEP 0085 chat state +const std::string NS_CHATSTATE("http://jabber.org/protocol/chatstates"); +const QName QN_CS_ACTIVE(true, NS_CHATSTATE, "active"); +const QName QN_CS_COMPOSING(true, NS_CHATSTATE, "composing"); +const QName QN_CS_PAUSED(true, NS_CHATSTATE, "paused"); +const QName QN_CS_INACTIVE(true, NS_CHATSTATE, "inactive"); +const QName QN_CS_GONE(true, NS_CHATSTATE, "gone"); + +// JEP 0091 Delayed Delivery +const std::string kNSDelay("jabber:x:delay"); +const QName kQnDelayX(true, kNSDelay, "x"); +const QName kQnStamp(true, STR_EMPTY, "stamp"); + +// Google time stamping (higher resolution) +const std::string kNSTimestamp("google:timestamp"); +const QName kQnTime(true, kNSTimestamp, "time"); +const QName kQnMilliseconds(true, STR_EMPTY, "ms"); + + + +// Jingle Info +const std::string NS_JINGLE_INFO("google:jingleinfo"); +const QName QN_JINGLE_INFO_QUERY(true, NS_JINGLE_INFO, "query"); +const QName QN_JINGLE_INFO_STUN(true, NS_JINGLE_INFO, "stun"); +const QName QN_JINGLE_INFO_RELAY(true, NS_JINGLE_INFO, "relay"); +const QName QN_JINGLE_INFO_SERVER(true, NS_JINGLE_INFO, "server"); +const QName QN_JINGLE_INFO_TOKEN(true, NS_JINGLE_INFO, "token"); +const QName QN_JINGLE_INFO_HOST(true, STR_EMPTY, "host"); +const QName QN_JINGLE_INFO_TCP(true, STR_EMPTY, "tcp"); +const QName QN_JINGLE_INFO_UDP(true, STR_EMPTY, "udp"); +const QName QN_JINGLE_INFO_TCPSSL(true, STR_EMPTY, "tcpssl"); + +} diff --git a/Plugins/jingle/libjingle/talk/xmpp/constants.h b/Plugins/jingle/libjingle/talk/xmpp/constants.h new file mode 100644 index 0000000..743e7ee --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/constants.h @@ -0,0 +1,358 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ +#define _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ + +#include <string> +#include "talk/xmllite/qname.h" +#include "talk/xmpp/jid.h" + + +#define NS_CLIENT STR_EMPTY // XXX Constants::ns_client() +#define NS_SERVER Constants::ns_server() +#define NS_STREAM Constants::ns_stream() +#define NS_XSTREAM Constants::ns_xstream() +#define NS_TLS Constants::ns_tls() +#define NS_SASL Constants::ns_sasl() +#define NS_BIND Constants::ns_bind() +#define NS_DIALBACK Constants::ns_dialback() +#define NS_SESSION Constants::ns_session() +#define NS_STANZA Constants::ns_stanza() +#define NS_PRIVACY Constants::ns_privacy() +#define NS_ROSTER Constants::ns_roster() +#define NS_VCARD Constants::ns_vcard() +#define NS_AVATAR_HASH Constants::ns_avatar_hash() +#define NS_VCARD_UPDATE Constants::ns_vcard_update() +#define STR_CLIENT Constants::str_client() +#define STR_SERVER Constants::str_server() +#define STR_STREAM Constants::str_stream() + + +namespace buzz { + +extern const Jid JID_EMPTY; + +class Constants { + public: + static const std::string & ns_client(); + static const std::string & ns_server(); + static const std::string & ns_stream(); + static const std::string & ns_xstream(); + static const std::string & ns_tls(); + static const std::string & ns_sasl(); + static const std::string & ns_bind(); + static const std::string & ns_dialback(); + static const std::string & ns_session(); + static const std::string & ns_stanza(); + static const std::string & ns_privacy(); + static const std::string & ns_roster(); + static const std::string & ns_vcard(); + static const std::string & ns_avatar_hash(); + static const std::string & ns_vcard_update(); + + static const std::string & str_client(); + static const std::string & str_server(); + static const std::string & str_stream(); +}; + +extern const std::string STR_GET; +extern const std::string STR_SET; +extern const std::string STR_RESULT; +extern const std::string STR_ERROR; + + +extern const std::string STR_FROM; +extern const std::string STR_TO; +extern const std::string STR_BOTH; +extern const std::string STR_REMOVE; + +extern const std::string STR_MESSAGE; +extern const std::string STR_BODY; +extern const std::string STR_PRESENCE; +extern const std::string STR_STATUS; +extern const std::string STR_SHOW; +extern const std::string STR_PRIOIRTY; +extern const std::string STR_IQ; + +extern const std::string STR_TYPE; +extern const std::string STR_NAME; +extern const std::string STR_ID; +extern const std::string STR_JID; +extern const std::string STR_SUBSCRIPTION; +extern const std::string STR_ASK; +extern const std::string STR_X; +extern const std::string STR_GOOGLE_COM; +extern const std::string STR_GMAIL_COM; +extern const std::string STR_GOOGLEMAIL_COM; +extern const std::string STR_DEFAULT_DOMAIN; +extern const std::string STR_TALK_GOOGLE_COM; +extern const std::string STR_TALKX_L_GOOGLE_COM; + +#ifdef FEATURE_ENABLE_VOICEMAIL +extern const std::string STR_VOICEMAIL; +extern const std::string STR_OUTGOINGVOICEMAIL; +#endif + +extern const std::string STR_UNAVAILABLE; + +extern const QName QN_STREAM_STREAM; +extern const QName QN_STREAM_FEATURES; +extern const QName QN_STREAM_ERROR; + +extern const QName QN_XSTREAM_BAD_FORMAT; +extern const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX; +extern const QName QN_XSTREAM_CONFLICT; +extern const QName QN_XSTREAM_CONNECTION_TIMEOUT; +extern const QName QN_XSTREAM_HOST_GONE; +extern const QName QN_XSTREAM_HOST_UNKNOWN; +extern const QName QN_XSTREAM_IMPROPER_ADDRESSIING; +extern const QName QN_XSTREAM_INTERNAL_SERVER_ERROR; +extern const QName QN_XSTREAM_INVALID_FROM; +extern const QName QN_XSTREAM_INVALID_ID; +extern const QName QN_XSTREAM_INVALID_NAMESPACE; +extern const QName QN_XSTREAM_INVALID_XML; +extern const QName QN_XSTREAM_NOT_AUTHORIZED; +extern const QName QN_XSTREAM_POLICY_VIOLATION; +extern const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED; +extern const QName QN_XSTREAM_RESOURCE_CONSTRAINT; +extern const QName QN_XSTREAM_RESTRICTED_XML; +extern const QName QN_XSTREAM_SEE_OTHER_HOST; +extern const QName QN_XSTREAM_SYSTEM_SHUTDOWN; +extern const QName QN_XSTREAM_UNDEFINED_CONDITION; +extern const QName QN_XSTREAM_UNSUPPORTED_ENCODING; +extern const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE; +extern const QName QN_XSTREAM_UNSUPPORTED_VERSION; +extern const QName QN_XSTREAM_XML_NOT_WELL_FORMED; +extern const QName QN_XSTREAM_TEXT; + +extern const QName QN_TLS_STARTTLS; +extern const QName QN_TLS_REQUIRED; +extern const QName QN_TLS_PROCEED; +extern const QName QN_TLS_FAILURE; + +extern const QName QN_SASL_MECHANISMS; +extern const QName QN_SASL_MECHANISM; +extern const QName QN_SASL_AUTH; +extern const QName QN_SASL_CHALLENGE; +extern const QName QN_SASL_RESPONSE; +extern const QName QN_SASL_ABORT; +extern const QName QN_SASL_SUCCESS; +extern const QName QN_SASL_FAILURE; +extern const QName QN_SASL_ABORTED; +extern const QName QN_SASL_INCORRECT_ENCODING; +extern const QName QN_SASL_INVALID_AUTHZID; +extern const QName QN_SASL_INVALID_MECHANISM; +extern const QName QN_SASL_MECHANISM_TOO_WEAK; +extern const QName QN_SASL_NOT_AUTHORIZED; +extern const QName QN_SASL_TEMPORARY_AUTH_FAILURE; + +extern const std::string NS_GOOGLE_AUTH; +extern const QName QN_MISSING_USERNAME; + +extern const QName QN_DIALBACK_RESULT; +extern const QName QN_DIALBACK_VERIFY; + +extern const QName QN_STANZA_BAD_REQUEST; +extern const QName QN_STANZA_CONFLICT; +extern const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED; +extern const QName QN_STANZA_FORBIDDEN; +extern const QName QN_STANZA_GONE; +extern const QName QN_STANZA_INTERNAL_SERVER_ERROR; +extern const QName QN_STANZA_ITEM_NOT_FOUND; +extern const QName QN_STANZA_JID_MALFORMED; +extern const QName QN_STANZA_NOT_ACCEPTABLE; +extern const QName QN_STANZA_NOT_ALLOWED; +extern const QName QN_STANZA_PAYMENT_REQUIRED; +extern const QName QN_STANZA_RECIPIENT_UNAVAILABLE; +extern const QName QN_STANZA_REDIRECT; +extern const QName QN_STANZA_REGISTRATION_REQUIRED; +extern const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND; +extern const QName QN_STANZA_REMOTE_SERVER_TIMEOUT; +extern const QName QN_STANZA_RESOURCE_CONSTRAINT; +extern const QName QN_STANZA_SERVICE_UNAVAILABLE; +extern const QName QN_STANZA_SUBSCRIPTION_REQUIRED; +extern const QName QN_STANZA_UNDEFINED_CONDITION; +extern const QName QN_STANZA_UNEXPECTED_REQUEST; +extern const QName QN_STANZA_TEXT; + +extern const QName QN_BIND_BIND; +extern const QName QN_BIND_RESOURCE; +extern const QName QN_BIND_JID; + +extern const QName QN_MESSAGE; +extern const QName QN_BODY; +extern const QName QN_SUBJECT; +extern const QName QN_THREAD; +extern const QName QN_PRESENCE; +extern const QName QN_SHOW; +extern const QName QN_STATUS; +extern const QName QN_LANG; +extern const QName QN_PRIORITY; +extern const QName QN_IQ; +extern const QName QN_ERROR; + +extern const QName QN_SERVER_MESSAGE; +extern const QName QN_SERVER_BODY; +extern const QName QN_SERVER_SUBJECT; +extern const QName QN_SERVER_THREAD; +extern const QName QN_SERVER_PRESENCE; +extern const QName QN_SERVER_SHOW; +extern const QName QN_SERVER_STATUS; +extern const QName QN_SERVER_LANG; +extern const QName QN_SERVER_PRIORITY; +extern const QName QN_SERVER_IQ; +extern const QName QN_SERVER_ERROR; + +extern const QName QN_SESSION_SESSION; + +extern const QName QN_PRIVACY_QUERY; +extern const QName QN_PRIVACY_ACTIVE; +extern const QName QN_PRIVACY_DEFAULT; +extern const QName QN_PRIVACY_LIST; +extern const QName QN_PRIVACY_ITEM; +extern const QName QN_PRIVACY_IQ; +extern const QName QN_PRIVACY_MESSAGE; +extern const QName QN_PRIVACY_PRESENCE_IN; +extern const QName QN_PRIVACY_PRESENCE_OUT; + +extern const QName QN_ROSTER_QUERY; +extern const QName QN_ROSTER_ITEM; +extern const QName QN_ROSTER_GROUP; + +extern const QName QN_VCARD; +extern const QName QN_VCARD_FN; +extern const QName QN_VCARD_PHOTO; +extern const QName QN_VCARD_PHOTO_BINVAL; +extern const QName QN_VCARD_AVATAR_HASH; +extern const QName QN_VCARD_AVATAR_HASH_MODIFIED; + + +extern const QName QN_XML_LANG; + +extern const QName QN_ENCODING; +extern const QName QN_VERSION; +extern const QName QN_TO; +extern const QName QN_FROM; +extern const QName QN_TYPE; +extern const QName QN_ID; +extern const QName QN_CODE; +extern const QName QN_NAME; +extern const QName QN_VALUE; +extern const QName QN_ACTION; +extern const QName QN_ORDER; +extern const QName QN_MECHANISM; +extern const QName QN_ASK; +extern const QName QN_JID; +extern const QName QN_SUBSCRIPTION; +extern const QName QN_TITLE1; +extern const QName QN_TITLE2; + + +extern const QName QN_XMLNS_CLIENT; +extern const QName QN_XMLNS_SERVER; +extern const QName QN_XMLNS_STREAM; + +// Presence +extern const std::string STR_SHOW_AWAY; +extern const std::string STR_SHOW_CHAT; +extern const std::string STR_SHOW_DND; +extern const std::string STR_SHOW_XA; +extern const std::string STR_SHOW_OFFLINE; + +// Subscription +extern const std::string STR_SUBSCRIBE; +extern const std::string STR_SUBSCRIBED; +extern const std::string STR_UNSUBSCRIBE; +extern const std::string STR_UNSUBSCRIBED; + + +// JEP 0030 +extern const QName QN_NODE; +extern const QName QN_CATEGORY; +extern const QName QN_VAR; +extern const std::string NS_DISCO_INFO; +extern const std::string NS_DISCO_ITEMS; + +extern const QName QN_DISCO_INFO_QUERY; +extern const QName QN_DISCO_IDENTITY; +extern const QName QN_DISCO_FEATURE; + +extern const QName QN_DISCO_ITEMS_QUERY; +extern const QName QN_DISCO_ITEM; + + +// JEP 0115 +extern const std::string NS_CAPS; +extern const QName QN_CAPS_C; +extern const QName QN_VER; +extern const QName QN_EXT; + + +// Avatar - JEP 0153 +extern const std::string kNSVCard; +extern const QName kQnVCardX; +extern const QName kQnVCardPhoto; + +// JEP 0172 User Nickname +extern const std::string kNSNickname; +extern const QName kQnNickname; + + +// JEP 0085 chat state +extern const std::string NS_CHATSTATE; +extern const QName QN_CS_ACTIVE; +extern const QName QN_CS_COMPOSING; +extern const QName QN_CS_PAUSED; +extern const QName QN_CS_INACTIVE; +extern const QName QN_CS_GONE; + +// JEP 0091 Delayed Delivery +extern const std::string kNSDelay; +extern const QName kQnDelayX; +extern const QName kQnStamp; + +// Google time stamping (higher resolution) +extern const std::string kNSTimestamp; +extern const QName kQnTime; +extern const QName kQnMilliseconds; + + +extern const std::string NS_JINGLE_INFO; +extern const QName QN_JINGLE_INFO_QUERY; +extern const QName QN_JINGLE_INFO_STUN; +extern const QName QN_JINGLE_INFO_RELAY; +extern const QName QN_JINGLE_INFO_SERVER; +extern const QName QN_JINGLE_INFO_TOKEN; +extern const QName QN_JINGLE_INFO_HOST; +extern const QName QN_JINGLE_INFO_TCP; +extern const QName QN_JINGLE_INFO_UDP; +extern const QName QN_JINGLE_INFO_TCPSSL; + +} + +#endif // _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_ diff --git a/Plugins/jingle/libjingle/talk/xmpp/jid.cc b/Plugins/jingle/libjingle/talk/xmpp/jid.cc new file mode 100644 index 0000000..ead2074 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/jid.cc @@ -0,0 +1,506 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +extern "C" { +#include <ctype.h> +} +#include <string> +#include "talk/xmpp/jid.h" +#include "talk/xmpp/constants.h" +#include "talk/base/common.h" +#include <algorithm> +#include "talk/base/logging.h" + +namespace buzz { + +static int AsciiToLower(int x) { + return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x; +} + +Jid::Jid() : data_(NULL) { +} + +Jid::Jid(bool is_special, const std::string & special) { + data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL; +} + +Jid::Jid(const std::string & jid_string) { + if (jid_string == STR_EMPTY) { + data_ = NULL; + return; + } + + // First find the slash and slice of that part + size_t slash = jid_string.find('/'); + std::string resource_name = (slash == std::string::npos ? STR_EMPTY : + jid_string.substr(slash + 1)); + + // Now look for the node + std::string node_name; + size_t at = jid_string.find('@'); + size_t domain_begin; + if (at < slash && at != std::string::npos) { + node_name = jid_string.substr(0, at); + domain_begin = at + 1; + } else { + domain_begin = 0; + } + + // Now take what is left as the domain + size_t domain_length = + ( slash == std::string::npos + ? jid_string.length() - domain_begin + : slash - domain_begin); + + // avoid allocating these constants repeatedly + std::string domain_name; + + if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) { + domain_name = STR_GMAIL_COM; + } + else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLEMAIL_COM; + } + else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) { + domain_name = STR_GOOGLE_COM; + } + else { + domain_name = jid_string.substr(domain_begin, domain_length); + } + + // If the domain is empty we have a non-valid jid and we should empty + // everything else out + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +Jid::Jid(const std::string & node_name, + const std::string & domain_name, + const std::string & resource_name) { + if (domain_name.empty()) { + data_ = NULL; + return; + } + + bool valid_node; + std::string validated_node = prepNode(node_name, + node_name.begin(), node_name.end(), &valid_node); + bool valid_domain; + std::string validated_domain = prepDomain(domain_name, + domain_name.begin(), domain_name.end(), &valid_domain); + bool valid_resource; + std::string validated_resource = prepResource(resource_name, + resource_name.begin(), resource_name.end(), &valid_resource); + + if (!valid_node || !valid_domain || !valid_resource) { + data_ = NULL; + return; + } + + data_ = new Data(validated_node, validated_domain, validated_resource); +} + +std::string Jid::Str() const { + if (!IsValid()) + return STR_EMPTY; + + std::string ret; + + if (!data_->node_name_.empty()) + ret = data_->node_name_ + "@"; + + ASSERT(data_->domain_name_ != STR_EMPTY); + ret += data_->domain_name_; + + if (!data_->resource_name_.empty()) + ret += "/" + data_->resource_name_; + + return ret; +} + +bool +Jid::IsValid() const { + return data_ != NULL && !data_->domain_name_.empty(); +} + +bool +Jid::IsBare() const { + if (Compare(JID_EMPTY) == 0) { + LOG(LS_VERBOSE) << "Warning: Calling IsBare() on the empty jid"; + return true; + } + return IsValid() && + data_->resource_name_.empty(); +} + +bool +Jid::IsFull() const { + return IsValid() && + !data_->resource_name_.empty(); +} + +Jid +Jid::BareJid() const { + if (!IsValid()) + return Jid(); + if (!IsFull()) + return *this; + return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY); +} + +#if 0 +void +Jid::set_node(const std::string & node_name) { + data_->node_name_ = node_name; +} +void +Jid::set_domain(const std::string & domain_name) { + data_->domain_name_ = domain_name; +} +void +Jid::set_resource(const std::string & res_name) { + data_->resource_name_ = res_name; +} +#endif + +bool +Jid::BareEquals(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_); +} + +bool +Jid::operator==(const Jid & other) const { + return (other.data_ == data_ || + data_ != NULL && + other.data_ != NULL && + other.data_->node_name_ == data_->node_name_ && + other.data_->domain_name_ == data_->domain_name_ && + other.data_->resource_name_ == data_->resource_name_); +} + +int +Jid::Compare(const Jid & other) const { + if (other.data_ == data_) + return 0; + if (data_ == NULL) + return -1; + if (other.data_ == NULL) + return 1; + + int compare_result; + compare_result = data_->node_name_.compare(other.data_->node_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->domain_name_.compare(other.data_->domain_name_); + if (0 != compare_result) + return compare_result; + compare_result = data_->resource_name_.compare(other.data_->resource_name_); + return compare_result; +} + +uint32 Jid::ComputeLameHash() const { + uint32 hash = 0; + // Hash the node portion + { + const std::string &str = node(); + for (int i = 0; i < static_cast<int>(str.size()); ++i) { + hash = ((hash << 2) + hash) + str[i]; + } + } + + // Hash the domain portion + { + const std::string &str = domain(); + for (int i = 0; i < static_cast<int>(str.size()); ++i) + hash = ((hash << 2) + hash) + str[i]; + } + + // Hash the resource portion + { + const std::string &str = resource(); + for (int i = 0; i < static_cast<int>(str.size()); ++i) + hash = ((hash << 2) + hash) + str[i]; + } + + return hash; +} + +// --- JID parsing code: --- + +// Checks and normalizes the node part of a JID. +std::string +Jid::prepNode(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + unsigned char ch = *i; + if (ch <= 0x7F) { + result += prepNodeAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += tolower(ch); + } + if (!char_valid) { + return STR_EMPTY; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Returns the appropriate mapping for an ASCII character in a node. +char +Jid::prepNodeAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case ' ': case '&': case '/': case ':': case '<': case '>': case '@': + case '\"': case '\'': + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + + +// Checks and normalizes the resource part of a JID. +std::string +Jid::prepResource(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + unsigned char ch = *i; + if (ch <= 0x7F) { + result += prepResourceAscii(ch, &char_valid); + } + else { + // TODO: implement the correct stringprep protocol for these + result += ch; + } + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + +// Returns the appropriate mapping for an ASCII character in a resource. +char +Jid::prepResourceAscii(char ch, bool *valid) { + *valid = true; + switch (ch) { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +// Checks and normalizes the domain part of a JID. +std::string +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, bool *valid) { + *valid = false; + std::string result; + + // TODO: if the domain contains a ':', then we should parse it + // as an IPv6 address rather than giving an error about illegal domain. + prepDomain(str, start, end, &result, valid); + if (!*valid) { + return STR_EMPTY; + } + + if (result.length() > 1023) { + return STR_EMPTY; + } + *valid = true; + return result; +} + + +// Checks and normalizes an IDNA domain. +void +Jid::prepDomain(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + std::string::const_iterator last = start; + for (std::string::const_iterator i = start; i < end; i++) { + bool label_valid = true; + char ch = *i; + switch (ch) { + case 0x002E: +#if 0 // FIX: This isn't UTF-8-aware. + case 0x3002: + case 0xFF0E: + case 0xFF61: +#endif + prepDomainLabel(str, last, i, buf, &label_valid); + *buf += '.'; + last = i + 1; + break; + } + if (!label_valid) { + return; + } + } + prepDomainLabel(str, last, end, buf, valid); +} + +// Checks and normalizes a domain label. +void +Jid::prepDomainLabel(const std::string str, std::string::const_iterator start, + std::string::const_iterator end, std::string *buf, bool *valid) { + *valid = false; + + int startLen = buf->length(); + for (std::string::const_iterator i = start; i < end; i++) { + bool char_valid = true; + unsigned char ch = *i; + if (ch <= 0x7F) { + *buf += prepDomainLabelAscii(ch, &char_valid); + } + else { + // TODO: implement ToASCII for these + *buf += ch; + } + if (!char_valid) { + return; + } + } + + int count = buf->length() - startLen; + if (count == 0) { + return; + } + else if (count > 63) { + return; + } + + // Is this check needed? See comment in prepDomainLabelAscii. + if ((*buf)[startLen] == '-') { + return; + } + if ((*buf)[buf->length() - 1] == '-') { + return; + } + *valid = true; +} + + +// Returns the appropriate mapping for an ASCII character in a domain label. +char +Jid::prepDomainLabelAscii(char ch, bool *valid) { + *valid = true; + // TODO: A literal reading of the spec seems to say that we do + // not need to check for these illegal characters (an "internationalized + // domain label" runs ToASCII with UseSTD3... set to false). But that + // can't be right. We should at least be checking that there are no '/' + // or '@' characters in the domain. Perhaps we should see what others + // do in this case. + + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + return (char)(ch + ('a' - 'A')); + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: + case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: + case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: + case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A: + case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: + case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: + case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: + *valid = false; + return 0; + + default: + return ch; + } +} + +} diff --git a/Plugins/jingle/libjingle/talk/xmpp/jid.h b/Plugins/jingle/libjingle/talk/xmpp/jid.h new file mode 100644 index 0000000..6831bda --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/jid.h @@ -0,0 +1,148 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ +#ifndef _jid_h_ +#define _jid_h_ + +#include <string> +#include "talk/base/basictypes.h" +#include "talk/xmllite/xmlconstants.h" + +namespace buzz { + +//! The Jid class encapsulates and provides parsing help for Jids +//! A Jid consists of three parts. The node, the domain and the resource. +//! +//! node@domain/resource +//! +//! The node and resource are both optional. A valid jid is defined to have +//! a domain. A bare jid is defined to not have a resource and a full jid +//! *does* have a resource. +class Jid { +public: + explicit Jid(); + explicit Jid(const std::string & jid_string); + explicit Jid(const std::string & node_name, + const std::string & domain_name, + const std::string & resource_name); + explicit Jid(bool special, const std::string & special_string); + Jid(const Jid & jid) : data_(jid.data_) { + if (data_ != NULL) { + data_->AddRef(); + } + } + Jid & operator=(const Jid & jid) { + if (jid.data_ != NULL) { + jid.data_->AddRef(); + } + if (data_ != NULL) { + data_->Release(); + } + data_ = jid.data_; + return *this; + } + ~Jid() { + if (data_ != NULL) { + data_->Release(); + } + } + + + const std::string & node() const { return !data_ ? STR_EMPTY : data_->node_name_; } + // void set_node(const std::string & node_name); + const std::string & domain() const { return !data_ ? STR_EMPTY : data_->domain_name_; } + // void set_domain(const std::string & domain_name); + const std::string & resource() const { return !data_ ? STR_EMPTY : data_->resource_name_; } + // void set_resource(const std::string & res_name); + + std::string Str() const; + Jid BareJid() const; + + bool IsValid() const; + bool IsBare() const; + bool IsFull() const; + + bool BareEquals(const Jid & other) const; + + bool operator==(const Jid & other) const; + bool operator!=(const Jid & other) const { return !operator==(other); } + + bool operator<(const Jid & other) const { return Compare(other) < 0; }; + bool operator>(const Jid & other) const { return Compare(other) > 0; }; + + int Compare(const Jid & other) const; + + // A quick and dirty hash. Don't count on this producing a great + // distribution. + uint32 ComputeLameHash() const; + +private: + + static std::string prepNode(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static char prepNodeAscii(char ch, bool *valid); + static std::string prepResource(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static char prepResourceAscii(char ch, bool *valid); + static std::string prepDomain(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + bool *valid); + static void prepDomain(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + std::string *buf, bool *valid); + static void prepDomainLabel(const std::string str, + std::string::const_iterator start, std::string::const_iterator end, + std::string *buf, bool *valid); + static char prepDomainLabelAscii(char ch, bool *valid); + + class Data { + public: + Data() : refcount_(1) {} + Data(const std::string & node, const std::string &domain, const std::string & resource) : + node_name_(node), + domain_name_(domain), + resource_name_(resource), + refcount_(1) {} + const std::string node_name_; + const std::string domain_name_; + const std::string resource_name_; + + void AddRef() { refcount_++; } + void Release() { if (!--refcount_) delete this; } + private: + int refcount_; + }; + + Data * data_; +}; + +} + + + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h b/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h new file mode 100644 index 0000000..e7d44b9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/plainsaslhandler.h @@ -0,0 +1,80 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _PLAINSASLHANDLER_H_ +#define _PLAINSASLHANDLER_H_ + +#include "talk/xmpp/saslhandler.h" +#include <algorithm> + +namespace buzz { + +class PlainSaslHandler : public SaslHandler { +public: + PlainSaslHandler(const Jid & jid, const talk_base::CryptString & password, + bool allow_plain) : jid_(jid), password_(password), + allow_plain_(allow_plain) {} + + virtual ~PlainSaslHandler() {} + + // Should pick the best method according to this handler + // returns the empty string if none are suitable + virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) { + + if (!encrypted && !allow_plain_) { + return ""; + } + + std::vector<std::string>::const_iterator it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN"); + if (it == mechanisms.end()) { + return ""; + } + else { + return "PLAIN"; + } + } + + // Creates a SaslMechanism for the given mechanism name (you own it + // once you get it). If not handled, return NULL. + virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) { + if (mechanism == "PLAIN") { + return new SaslPlainMechanism(jid_, password_); + } + return NULL; + } + +private: + Jid jid_; + talk_base::CryptString password_; + bool allow_plain_; +}; + + +} + +#endif + diff --git a/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h b/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h new file mode 100644 index 0000000..f94bd3d --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/prexmppauth.h @@ -0,0 +1,86 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _PREXMPPAUTH_H_ +#define _PREXMPPAUTH_H_ + +#include "talk/base/cryptstring.h" +#include "talk/base/sigslot.h" +#include "talk/xmpp/saslhandler.h" + +namespace talk_base { + class SocketAddress; +} + +namespace buzz { + +class Jid; +class SaslMechanism; + +class CaptchaChallenge { + public: + CaptchaChallenge() : captcha_needed_(false) {} + CaptchaChallenge(const std::string& token, const std::string& url) + : captcha_needed_(true), captcha_token_(token), captcha_image_url_(url) { + } + + bool captcha_needed() const { return captcha_needed_; } + const std::string& captcha_token() const { return captcha_token_; } + + // This url is relative to the gaia server. Once we have better tools + // for cracking URLs, we should probably make this a full URL + const std::string& captcha_image_url() const { return captcha_image_url_; } + + private: + bool captcha_needed_; + std::string captcha_token_; + std::string captcha_image_url_; +}; + +class PreXmppAuth : public SaslHandler { +public: + virtual ~PreXmppAuth() {} + + virtual void StartPreXmppAuth( + const Jid & jid, + const talk_base::SocketAddress & server, + const talk_base::CryptString & pass, + const std::string & auth_cookie) = 0; + + sigslot::signal0<> SignalAuthDone; + + virtual bool IsAuthDone() = 0; + virtual bool IsAuthorized() = 0; + virtual bool HadError() = 0; + virtual int GetError() = 0; + virtual CaptchaChallenge GetCaptchaChallenge() = 0; + virtual std::string GetAuthCookie() = 0; +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc new file mode 100644 index 0000000..81c55ac --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.cc @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/xmpp/ratelimitmanager.h" + +namespace buzz { + +RateLimitManager::RateLimit* RateLimitManager::GetRateLimit( + const std::string event_name) { + RateLimitMap::iterator it = rate_limits_.find(event_name); + if (it != rate_limits_.end()) { + return it->second; + } + return NULL; +} + +bool RateLimitManager::IsWithinRateLimit(const std::string event_name) { + RateLimit* current_rate = GetRateLimit(event_name); + if (current_rate) { + return current_rate->IsWithinRateLimit(); + } + return true; // If no rate limit is set, then you must be under the limit +} + +void RateLimitManager::UpdateRateLimit(const std::string event_name, + int max_count, + int per_x_seconds) { + RateLimit* current_rate = GetRateLimit(event_name); + if (!current_rate) { + current_rate = new RateLimit(max_count, per_x_seconds); + rate_limits_[event_name] = current_rate; + } + current_rate->UpdateRateLimit(); +} + +bool RateLimitManager::VerifyRateLimit(const std::string event_name, + int max_count, + int per_x_seconds) { + return VerifyRateLimit(event_name, max_count, per_x_seconds, false); +} + +bool RateLimitManager::VerifyRateLimit(const std::string event_name, + int max_count, + int per_x_seconds, + bool always_update) { + bool within_rate_limit = IsWithinRateLimit(event_name); + if (within_rate_limit || always_update) { + UpdateRateLimit(event_name, max_count, per_x_seconds); + } + return within_rate_limit; +} + +} diff --git a/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h new file mode 100644 index 0000000..79960d8 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/ratelimitmanager.h @@ -0,0 +1,146 @@ +/* + * libjingle + * Copyright 2004--2006, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _RATELIMITMANAGER_H_ +#define _RATELIMITMANAGER_H_ + +#include "talk/base/time.h" +#include "talk/base/taskrunner.h" +#include <map> + +namespace buzz { + +///////////////////////////////////////////////////////////////////// +// +// RATELIMITMANAGER +// +///////////////////////////////////////////////////////////////////// +// +// RateLimitManager imposes client-side rate limiting for xmpp tasks and +// other events. It ensures that no more than i events with a given name +// can occur within k seconds. +// +// A buffer tracks the previous max_count events. Before an event is allowed +// to occur, it can check its rate limit with a call to VerifyRateLimit. +// VerifyRateLimit will look up the i-th to last event and if more than +// k seconds have passed since then, it will return true and update the +// appropriate rate limits. Else, it will return false. +// +///////////////////////////////////////////////////////////////////// + +class RateLimitManager { + public: + + RateLimitManager() { }; + ~RateLimitManager() { + for (RateLimitMap::iterator it = rate_limits_.begin(); + it != rate_limits_.end(); ++it) { + delete it->second; + } + }; + + // Checks if the event is under the defined rate limit and updates the + // rate limit if so. Returns true if it's under the rate limit. + bool VerifyRateLimit(const std::string event_name, int max_count, + int per_x_seconds); + + // Checks if the event is under the defined rate limit and updates the + // rate limit if so *or* if always_update = true. + bool VerifyRateLimit(const std::string event_name, int max_count, + int per_x_seconds, bool always_update); + + private: + class RateLimit { + public: + RateLimit(int max, int per_x_secs) : counter_(0), max_count_(max), + per_x_seconds_(per_x_secs) { + event_times_ = new uint32[max_count_]; + for (int i = 0; i < max_count_; i++) { + event_times_[i] = 0; + } + } + + ~RateLimit() { + if (event_times_) { + delete[] event_times_; + } + } + + // True iff the current time >= to the next song allowed time + bool IsWithinRateLimit() { + uint32 current_time = talk_base::Time(); + if (talk_base::TimeDiff(current_time, NextTimeAllowedForCounter()) >= 0) { + return true; + } else { + return false; + } + } + + // Updates time and counter for rate limit + void UpdateRateLimit() { + event_times_[counter_] = talk_base::Time(); + counter_ = (counter_ + 1) % max_count_; + } + + private: + + // The time at which the i-th (where i = max_count) event occured + uint32 PreviousTimeAtCounter() { + return event_times_[counter_]; + } + + // The time that the next event is allowed to occur + uint32 NextTimeAllowedForCounter() { + return PreviousTimeAtCounter() + per_x_seconds_ * talk_base::kSecToMsec; + } + + int counter_; // count modulo max_count of the current event + int max_count_; // max number of events that can occur within per_x_seconds + int per_x_seconds_; // interval size for rate limit + uint32* event_times_; // buffer of previous max_count event + }; + + typedef std::map<const std::string, RateLimit*> RateLimitMap; + + // Maps from event name to its rate limit + RateLimitMap rate_limits_; + + // Returns rate limit for event with specified name + RateLimit* GetRateLimit(const std::string event_name); + + // True iff the current time >= to the next song allowed time + bool IsWithinRateLimit(const std::string event_name); + + // Updates time and counter for rate limit + void UpdateRateLimit(const std::string event_name, int max_count, + int per_x_seconds); + +}; + +} + +#endif //_RATELIMITMANAGER_H_ diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h new file mode 100644 index 0000000..a6630d9 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/saslcookiemechanism.h @@ -0,0 +1,67 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SASLCOOKIEMECHANISM_H_ +#define _SASLCOOKIEMECHANISM_H_ + +#include "talk/xmpp/saslmechanism.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" + +namespace buzz { + +class SaslCookieMechanism : public SaslMechanism { + +public: + SaslCookieMechanism(const std::string & mechanism, const std::string & username, const std::string & cookie) : + mechanism_(mechanism), username_(username), cookie_(cookie) {} + + virtual std::string GetMechanismName() { return mechanism_; } + + virtual XmlElement * StartSaslAuth() { + // send initial request + XmlElement * el = new XmlElement(QN_SASL_AUTH, true); + el->AddAttr(QN_MECHANISM, mechanism_); + + std::string credential; + credential.append("\0", 1); + credential.append(username_); + credential.append("\0", 1); + credential.append(cookie_); + el->AddText(Base64Encode(credential)); + return el; + } + +private: + std::string mechanism_; + std::string username_; + std::string cookie_; +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h b/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h new file mode 100644 index 0000000..b57d3ba --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/saslhandler.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SASLHANDLER_H_ +#define _SASLHANDLER_H_ + +#include <string> + +namespace buzz { + +class XmlElement; +class SaslMechanism; + +// Creates mechanisms to deal with a given mechanism +class SaslHandler { + +public: + + // Intended to be subclassed + virtual ~SaslHandler() {} + + // Should pick the best method according to this handler + // returns the empty string if none are suitable + virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) = 0; + + // Creates a SaslMechanism for the given mechanism name (you own it + // once you get it). + // If not handled, return NULL. + virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) = 0; +}; + +} + +#endif + diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc new file mode 100644 index 0000000..45c947a --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.cc @@ -0,0 +1,70 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "talk/base/base64.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/saslmechanism.h" + +using talk_base::Base64; + +namespace buzz { + +XmlElement * +SaslMechanism::StartSaslAuth() { + return new XmlElement(QN_SASL_AUTH, true); +} + +XmlElement * +SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) { + return new XmlElement(QN_SASL_ABORT, true); +} + +void +SaslMechanism::HandleSaslSuccess(const XmlElement * success) { +} + +void +SaslMechanism::HandleSaslFailure(const XmlElement * failure) { +} + +std::string +SaslMechanism::Base64Encode(const std::string & plain) { + return Base64::encode(plain); +} + +std::string +SaslMechanism::Base64Decode(const std::string & encoded) { + return Base64::decode(encoded); +} + +std::string +SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) { + return Base64::encodeFromArray(plain, length); +} + +} diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h new file mode 100644 index 0000000..f2e5adc --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/saslmechanism.h @@ -0,0 +1,74 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SASLMECHANISM_H_ +#define _SASLMECHANISM_H_ + +#include <string> + +namespace buzz { + +class XmlElement; + + +// Defines a mechnanism to do SASL authentication. +// Subclass instances should have a self-contained way to present +// credentials. +class SaslMechanism { + +public: + + // Intended to be subclassed + virtual ~SaslMechanism() {} + + // Should return the name of the SASL mechanism, e.g., "PLAIN" + virtual std::string GetMechanismName() = 0; + + // Should generate the initial "auth" request. Default is just <auth/>. + virtual XmlElement * StartSaslAuth(); + + // Should respond to a SASL "<challenge>" request. Default is + // to abort (for mechanisms that do not do challenge-response) + virtual XmlElement * HandleSaslChallenge(const XmlElement * challenge); + + // Notification of a SASL "<success>". Sometimes information + // is passed on success. + virtual void HandleSaslSuccess(const XmlElement * success); + + // Notification of a SASL "<failure>". Sometimes information + // for the user is passed on failure. + virtual void HandleSaslFailure(const XmlElement * failure); + +protected: + static std::string Base64Encode(const std::string & plain); + static std::string Base64Decode(const std::string & encoded); + static std::string Base64EncodeFromArray(const char * plain, size_t length); +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h b/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h new file mode 100644 index 0000000..72532e6 --- /dev/null +++ b/Plugins/jingle/libjingle/talk/xmpp/saslplainmechanism.h @@ -0,0 +1,65 @@ +/* + * libjingle + * Copyright 2004--2005, Google Inc. + * + * 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 name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SASLPLAINMECHANISM_H_ +#define _SASLPLAINMECHANISM_H_ + +#include "talk/base/cryptstring.h" +#include "talk/xmpp/saslmechanism.h" + +namespace buzz { + +class SaslPlainMechanism : public SaslMechanism { + +public: + SaslPlainMechanism(const buzz::Jid user_jid, const talk_base::CryptString & password) : + user_jid_(user_jid), password_(password) {} + + virtual std::string GetMechanismName() { return "PLAIN"; } + + virtual XmlElement * StartSaslAuth() { + // send initial request + XmlElement * el = new XmlElement(QN_SASL_AUTH, true); + el->AddAttr(QN_MECHANISM, "PLAIN"); + + talk_base::FormatCryptString credential; + credential.Append("\0", 1); + credential.Append(user_jid_.node()); + credential.Append("\0", 1); + credential.Append(&password_); + el->AddText(Base64EncodeFromArray(credential.GetData(), credential.GetLength())); + return el; + } + +private: + Jid user_jid_; + talk_base::CryptString password_; +}; + +} + +#endif diff --git a/Plugins/jingle/libjingle_callclient.cpp b/Plugins/jingle/libjingle_callclient.cpp new file mode 100644 index 0000000..fcafd8f --- /dev/null +++ b/Plugins/jingle/libjingle_callclient.cpp @@ -0,0 +1,281 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + +#include "commons.h" + + + + +CallClient::CallClient(DATA *aData) +{ + data = aData; + phone_client_ = NULL; + signaling = false; + needSignal = false; +} + +CallClient::~CallClient() +{ +} + +void CallClient::StartSignaling() +{ + if (!signaling && needSignal) + data->session_manager_->OnSignalingReady(); + + signaling = true; +} + +void CallClient::StopSignaling() +{ + signaling = false; + needSignal = false; +} + +void CallClient::OnSignalingReady() +{ + if (signaling) + data->session_manager_->OnSignalingReady(); + else + needSignal = true; +} + +void CallClient::OnConnect() +{ + std::string jid = ToString(data->fullJID); + cricket::InitRandom(jid.c_str(), jid.size());
+
+ phone_client_ = new cricket::PhoneSessionClient(buzz::Jid(jid), data->session_manager_);
+ phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); +} + +void CallClient::OnDisconnect() +{ + if (phone_client_ != NULL) + { + delete phone_client_; + phone_client_ = NULL; + } +} + +void CallClient::OnCallCreate(cricket::Call* call) +{ + call->SignalSessionState.connect(this, &CallClient::OnSessionState); +} + +void CallClient::OnCallDestroy(cricket::Call* call) +{ +} + +#ifdef UNICODE + +static WCHAR *mir_dupToUnicode(const char *ptr)
+{
+ if (ptr == NULL)
+ return NULL;
+
+ size_t size = strlen(ptr) + 1;
+ WCHAR *tmp = (WCHAR *) mir_alloc(size * sizeof(WCHAR));
+
+ MultiByteToWideChar(CP_ACP, 0, ptr, -1, tmp, size * sizeof(WCHAR));
+
+ return tmp;
+}
+ +#endif + +void CallClient::NofifyState(cricket::Call* call, + cricket::Session* session, + int state) +{ + char id[20];
+ VOICE_CALL vc = {0};
+ vc.cbSize = sizeof(vc);
+ vc.szModule = data->jabber->protocolName;
+ vc.id = itoa(call->id(), id, 10);
+ vc.flags = VOICE_CALL_CONTACT;
+ vc.state = state;
+#ifdef UNICODE + TCHAR *jid = mir_dupToUnicode(session->remote_name().c_str()); + vc.hContact = data->jabber->pfHContactFromJID(jid);
+ mir_free(jid);
+#else
+ vc.hContact = data->jabber->pfHContactFromJID(session->remote_name().c_str());
+#endif
+ NotifyEventHooks(data->hVoiceNotify, (WPARAM) &vc, 0);
+} + +void CallClient::OnSessionState(cricket::Call* call, + cricket::Session* session, + cricket::Session::State state) +{ + if (state == cricket::Session::STATE_RECEIVEDINITIATE) { + NofifyState(call, session, VOICE_STATE_RINGING); + + } else if (state == cricket::Session::STATE_SENTINITIATE) { + NofifyState(call, session, VOICE_STATE_CALLING); + + } else if (state == cricket::Session::STATE_SENTACCEPT + || state == cricket::Session::STATE_RECEIVEDACCEPT) { + StartSignaling();
+
+ } else if (state == cricket::Session::STATE_INPROGRESS) { + NofifyState(call, session, VOICE_STATE_TALKING); +
+ } else if (state == cricket::Session::STATE_DEINIT) { + NofifyState(call, session, VOICE_STATE_ENDED); + StopSignaling();
+ } +} + +void CallClient::OnMessage(talk_base::Message *pmsg) +{ + switch(pmsg->message_id) + { + case MSG_TIMER: + { + EnterCriticalSection(&data->csPendingIqMap);
+
+ time_t expire = time(NULL) - 3;
+ for(PendingIqMap::iterator it = data->pendingIqs.begin(); it != data->pendingIqs.end(); )
+ {
+ PendingIq &piq = it->second;
+ if (piq.sent < expire)
+ {
+ buzz::XmlElement *stanza = (buzz::XmlElement *) piq.param;
+ data->session_manager_->OnFailedSend(stanza, NULL);
+ delete stanza;
+
+ it = data->pendingIqs.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ LeaveCriticalSection(&data->csPendingIqMap);
+ break; + } + case MSG_REPLY_MSG: + { + ReplyMessageData *rmd = (ReplyMessageData *) pmsg->pdata; +
+ if (rmd->newStanza->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) { + data->session_manager_->OnIncomingResponse(rmd->oldStanza, rmd->newStanza); + } else { + data->session_manager_->OnFailedSend(rmd->oldStanza, rmd->newStanza); + } +
+ delete rmd->oldStanza;
+ delete rmd->newStanza;
+ delete rmd;
+ + break; + } + case MSG_INCOMING_MSG: + { + buzz::XmlElement *el = (buzz::XmlElement *) pmsg->pdata; + if (el == NULL) + return; +
+ if (data->session_manager_->IsSessionMessage(el))
+ data->session_manager_->OnIncomingMessage(el);
+
+ delete el;
+ break; + } + case MSG_MAKE_CALL_TO: + { + HANDLE hContact = (HANDLE) pmsg->pdata; + if ( hContact == NULL ) + return; + + DBVARIANT dbv;
+ if (DBGetContactSettingTString(hContact, data->jabber->protocolName, "jid", &dbv))
+ return;
+
+ TCHAR jid[512];
+ _tcsncpy(jid, dbv.ptszVal, MAX_REGS(jid));
+ DBFreeVariant(&dbv); + + if (DBGetContactSettingWord(hContact, data->jabber->protocolName, "Status", ID_STATUS_OFFLINE) <= ID_STATUS_OFFLINE)
+ return;
+
+ TCHAR szJid[512];
+ const TCHAR *bestResName = data->jabber->pfGetBestClientResourceNamePtr(jid);
+ mir_sntprintf(szJid, MAX_REGS(szJid), bestResName ? _T("%s/%s") : _T("%s"), jid, bestResName);
+ + phone_client_->SignalCallDestroy.connect(this, &CallClient::OnCallDestroy); + cricket::Call * call = phone_client_->CreateCall(); + call->InitiateSession(buzz::Jid(ToString(szJid)), NULL); + phone_client_->SetFocus(call); + break; + } + case MSG_ANSWER_CALL: + { + uint32 id = (uint32) pmsg->pdata; + cricket::Call * call = phone_client_->GetCall(id);
+ if (call == NULL)
+ return;
+
+ call->AcceptSession(call->sessions()[0]); + phone_client_->SetFocus(call); + break; + } + case MSG_DROP_CALL: + { + uint32 id = (uint32) pmsg->pdata; + cricket::Call * call = phone_client_->GetCall(id);
+ if (call == NULL)
+ return;
+
+ if (call->sessions()[0]->state() == cricket::Session::STATE_RECEIVEDINITIATE
+ || call->sessions()[0]->state() == cricket::Session::STATE_RECEIVEDMODIFY)
+ call->RejectSession(call->sessions()[0]); + else + call->Terminate(); + break; + } + case TERMINATE_ALL: + {
+ data->session_manager_->TerminateAll();
+ break; + } + } +} + +void CallClient::MakeCallTo(HANDLE hContact) +{ + if ( hContact == NULL ) + return; + + data->signaling_thread_->Post(this, MSG_MAKE_CALL_TO, (talk_base::MessageData *) hContact); +} + +void CallClient::AnswerCall(int id) +{ + data->signaling_thread_->Post(this, MSG_ANSWER_CALL, (talk_base::MessageData *) id); +} + +void CallClient::DropCall(int id) +{ + data->signaling_thread_->Post(this, MSG_DROP_CALL, (talk_base::MessageData *) id); +} + diff --git a/Plugins/jingle/libjingle_callclient.h b/Plugins/jingle/libjingle_callclient.h new file mode 100644 index 0000000..c85dee0 --- /dev/null +++ b/Plugins/jingle/libjingle_callclient.h @@ -0,0 +1,88 @@ +/* + * Jingle call example + * Copyright 2004--2005, Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CALLCLIENT_H__ +#define __CALLCLIENT_H__ + + +namespace talk_base { + class Thread; + class NetworkManager; +} + +namespace cricket { + class PortAllocator; + class PhoneSessionClient; + class Receiver; + class Call; + class SessionManagerTask; +} + +#define MSG_MAKE_CALL_TO 11 +#define MSG_ANSWER_CALL 12 +#define MSG_DROP_CALL 13 +#define MSG_INCOMING_MSG 14 +#define MSG_TIMER 15 +#define MSG_REPLY_MSG 16 +#define TERMINATE_ALL 17 + +
+class ReplyMessageData : public talk_base::MessageData { +public: + buzz::XmlElement *oldStanza; + buzz::XmlElement *newStanza; +}; + + + +class CallClient : public sigslot::has_slots<>, public talk_base::MessageHandler { +public: + CallClient(DATA *aData); + virtual ~CallClient(); + + void OnConnect(); + void OnDisconnect(); + + void MakeCallTo(HANDLE hContact); + void AnswerCall(int id); + void DropCall(int id); + void OnSignalingReady(); + + virtual void OnMessage(talk_base::Message *pmsg); + +private: + DATA *data; + + bool signaling; + bool needSignal; + + void StartSignaling(); + void StopSignaling(); + + cricket::PhoneSessionClient *phone_client_; + + void NofifyState(cricket::Call* call, cricket::Session* session, int state); + + void OnCallCreate(cricket::Call* call); + void OnCallDestroy(cricket::Call* call); + void OnSessionState(cricket::Call* call, cricket::Session* session, cricket::Session::State state); +}; + + +#endif // __CALLCLIENT_H__ diff --git a/Plugins/jingle/sdk/m_updater.h b/Plugins/jingle/sdk/m_updater.h new file mode 100644 index 0000000..371b743 --- /dev/null +++ b/Plugins/jingle/sdk/m_updater.h @@ -0,0 +1,146 @@ +#ifndef _M_UPDATER_H
+#define _M_UPDATER_H
+
+// NOTES:
+// - For langpack updates, include a string of the following format in the langpack text file:
+// ";FLID: <file listing name> <version>"
+// version must be four numbers seperated by '.', in the range 0-255 inclusive
+// - Updater will disable plugins that are downloaded but were not active prior to the update (this is so that, if an archive contains e.g. ansi and
+// unicode versions, the correct plugin will be the only one active after the new version is installed)...so if you add a support plugin, you may need
+// to install an ini file to make the plugin activate when miranda restarts after the update
+// - Updater will replace all dlls that have the same internal shortName as a downloaded update dll (this is so that msn1.dll and msn2.dll, for example,
+// will both be updated) - so if you have a unicode and a non-unicode version of a plugin in your archive, you should make the internal names different (which will break automatic
+// updates from the file listing if there is only one file listing entry for both versions, unless you use the 'MS_UPDATE_REGISTER' service below)
+// - Updater will install all files in the root of the archive into the plugins folder, except for langpack files that contain the FLID string which go into the root folder (same
+// folder as miranda32.exe)...all folders in the archive will also be copied to miranda's root folder, and their contents transferred into the new folders. The only exception is a
+// special folder called 'root_files' - if there is a folder by that name in the archive, it's contents will also be copied into miranda's root folder - this is intended to be used
+// to install additional dlls etc that a plugin may require)
+
+// if you set Update.szUpdateURL to the following value when registering, as well as setting your beta site and version data,
+// Updater will ignore szVersionURL and pbVersionPrefix, and attempt to find the file listing URL's from the backend XML data.
+// for this to work, the plugin name in pluginInfo.shortName must match the file listing exactly (except for case)
+#define UPDATER_AUTOREGISTER "UpdaterAUTOREGISTER"
+// Updater will also use the backend xml data if you provide URL's that reference the miranda file listing for updates (so you can use that method
+// if e.g. your plugin shortName does not match the file listing) - it will grab the file listing id from the end of these URLs
+
+typedef struct Update_tag {
+ int cbSize;
+ char *szComponentName; // component name as it will appear in the UI (will be translated before displaying)
+
+ char *szVersionURL; // URL where the current version can be found (NULL to disable)
+ BYTE *pbVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ // (note that this URL could point at a binary file - dunno why, but it could :)
+ int cpbVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szUpdateURL; // URL where dll/zip is located
+ // set to UPDATER_AUTOREGISTER if you want Updater to find the file listing URLs (ensure plugin shortName matches file listing!)
+
+ char *szBetaVersionURL; // URL where the beta version can be found (NULL to disable betas)
+ BYTE *pbBetaVersionPrefix; // bytes occuring in VersionURL before the version, used to locate the version information within the URL data
+ int cpbBetaVersionPrefix; // number of bytes pointed to by pbVersionPrefix
+ char *szBetaUpdateURL; // URL where dll/zip is located
+
+ BYTE *pbVersion; // bytes of current version, used for comparison with those in VersionURL
+ int cpbVersion; // number of bytes pointed to by pbVersion
+
+ char *szBetaChangelogURL; // url for displaying changelog for beta versions
+} Update;
+
+// register a comonent with Updater
+//
+// wparam = 0
+// lparam = (LPARAM)&Update
+#define MS_UPDATE_REGISTER "Update/Register"
+
+// utility functions to create a version string from a DWORD or from pluginInfo
+// point buf at a buffer at least 16 chars wide - but note the version string returned may be shorter
+//
+__inline static char *CreateVersionString(DWORD version, char *buf) {
+ mir_snprintf(buf, 16, "%d.%d.%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ return buf;
+}
+
+__inline static char *CreateVersionStringPlugin(PLUGININFO *pluginInfo, char *buf) {
+ return CreateVersionString(pluginInfo->version, buf);
+}
+
+
+// register the 'easy' way - use this method if you have no beta URL and the plugin is on the miranda file listing
+// NOTE: the plugin version string on the file listing must be the string version of the version in pluginInfo (i.e. 0.0.0.1,
+// four numbers between 0 and 255 inclusivem, so no letters, brackets, etc.)
+//
+// wParam = (int)fileID - this is the file ID from the file listing (i.e. the number at the end of the download link)
+// lParam = (PLUGININFO*)&pluginInfo
+#define MS_UPDATE_REGISTERFL "Update/RegisterFL"
+
+// this function can be used to 'unregister' components - useful for plugins that register non-plugin/langpack components and
+// may need to change those components on the fly
+// lParam = (char *)szComponentName
+#define MS_UPDATE_UNREGISTER "Update/Unregister"
+
+// this event is fired when the startup process is complete, but NOT if a restart is imminent
+// it is designed for status managment plugins to use as a trigger for beggining their own startup process
+// wParam = lParam = 0 (unused)
+// (added in version 0.1.6.0)
+#define ME_UPDATE_STARTUPDONE "Update/StartupDone"
+
+// this service can be used to enable/disable Updater's global status control
+// it can be called from the StartupDone event handler
+// wParam = (BOOL)enable
+// lParam = 0
+// (added in version 0.1.6.0)
+#define MS_UPDATE_ENABLESTATUSCONTROL "Update/EnableStatusControl"
+
+// An description of usage of the above service and event:
+// Say you are a status control plugin that normally sets protocol or global statuses in your ModulesLoaded event handler.
+// In order to make yourself 'Updater compatible', you would move the status control code from ModulesLoaded to another function,
+// say DoStartup. Then, in ModulesLoaded you would check for the existence of the MS_UPDATE_ENABLESTATUSCONTROL service.
+// If it does not exist, call DoStartup. If it does exist, hook the ME_UPDATE_STARTUPDONE event and call DoStartup from there. You may
+// also wish to call MS_UPDATE_ENABLESTATUSCONTROL with wParam == FALSE at this time, to disable Updater's own status control feature.
+
+// this service can be used to determine whether updates are possible for a component with the given name
+// wParam = 0
+// lParam = (char *)szComponentName
+// returns TRUE if updates are supported, FALSE otherwise
+#define MS_UPDATE_ISUPDATESUPPORTED "Update/IsUpdateSupported"
+
+#endif
+
+
+/////////////// Usage Example ///////////////
+
+#ifdef EXAMPLE_CODE
+
+// you need to #include "m_updater.h" and HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded) in your Load function...
+
+int OnModulesLoaded(WPARAM wParam, LPARAM lParam) {
+
+ Update update = {0}; // for c you'd use memset or ZeroMemory...
+ char szVersion[16];
+
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionString(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+ update.szUpdateURL = "http://scottellis.com.au:81/test/updater.zip";
+ update.szVersionURL = "http://scottellis.com.au:81/test/updater_test.html";
+ update.pbVersionPrefix = (BYTE *)"Updater version ";
+
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+
+ // Alternatively, to register a plugin with e.g. file ID 2254 on the file listing...
+ // CallService(MS_UPDATE_REGISTERFL, (WPARAM)2254, (LPARAM)&pluginInfo);
+
+ return 0;
+}
+
+#endif
diff --git a/Plugins/jingle/sdk/m_voice.h b/Plugins/jingle/sdk/m_voice.h new file mode 100644 index 0000000..a81d866 --- /dev/null +++ b/Plugins/jingle/sdk/m_voice.h @@ -0,0 +1,158 @@ +/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICE_H__
+# define __M_VOICE_H__
+
+
+#define EVENTTYPE_VOICE_CALL 8739
+
+
+#define PROTOTYPE_VOICE (PROTOTYPE_ENCRYPTION-9)
+
+
+#define VOICE_UNICODE 0x80000000
+
+#ifdef UNICODE
+# define VOICE_TCHAR VOICE_UNICODE
+#else
+# define VOICE_TCHAR 0
+#endif
+
+#define VOICE_STATE_TALKING 0
+#define VOICE_STATE_RINGING 1
+#define VOICE_STATE_CALLING 2
+#define VOICE_STATE_ON_HOLD 3
+#define VOICE_STATE_ENDED 4
+
+typedef struct {
+ int cbSize; // Struct size
+ const char *szModule; // The name of the protocol module (used only in notifications)
+ char *id; // Protocol especific ID for this call
+ int flags; // Can be VOICE_CALL_CONTACT or VOICE_CALL_STRING (VOICE_UNICODE to say the string is unicode)
+ union { // Who to call
+ HANDLE hContact;
+ TCHAR *ptszContact;
+ char *pszContact;
+ WCHAR *pwszContact;
+ };
+ int state; // VOICE_STATE_*
+
+} VOICE_CALL;
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define PE_VOICE_CALL_STATE "/Voice/State"
+
+
+#define VOICE_SUPPORTED 1 // Set if proto support voice calls. Probabilly will be 1 ;)
+#define VOICE_CALL_CONTACT 2 // Set if a call can be made to a hContact
+#define VOICE_CALL_CONTACT_NEED_TEST 4 // Set if the contact need to be tested with PS_VOICE_CALL_CONTACT_VALID (needs VOICE_CALL_CONTACT set to work)
+#define VOICE_CALL_STRING 8 // Set if a call can be made to some string (PS_VOICE_CALL_STRING_VALID is used to validate the string)
+#define VOICE_CAN_SET_DEVICE 16 // Set if the devices to mic in and sound out can be set (or the protocol will handle it internally)
+#define VOICE_CAN_HOLD 32 // Set if a call can be put on hold
+/*
+Get protocol voice support flags
+
+wParam: ignored
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_GETINFO "/Voice/GetInfo"
+
+/*
+Request to the protocol a voice call to hContact.
+
+wParam: (HANDLE) hContact
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_CALL "/Voice/Call"
+
+/*
+Service called to make the protocol answer a call.
+It is an async call. If the call was answered, the PE_VOICE_STARTEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_ANSWERCALL "/Voice/AnswerCall"
+
+/*
+Service called to make the protocol answer a call. This can be called if the
+call is ringing or has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_ENDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_DROPCALL "/Voice/DropCall"
+
+/*
+Service called to make the protocol hold a call. This means that the call should not
+be droped, but it should be muted and put in a hold, to allow other call to be answered.
+If the protocol can't hold a cal, it should be droped.
+
+This can be called if the call has started. If called any other time it should be ignored.
+It is an async call. If the call was droped, the PE_VOICE_HOLDEDCALL
+notification will be fired.
+
+wParam: (const char *) id
+lParam: ignored
+return: 0 on success
+*/
+#define PS_VOICE_HOLDCALL "/Voice/HoldCall"
+
+/*
+Used if protocol support VOICE_CALL_STRING. The call string is passed as
+wParam and the proto should validate it.
+
+wParam: (const TCHAR *) call string
+lParam: ignored
+return: 0 if wrong, 1 if correct
+*/
+#define PS_VOICE_CALL_STRING_VALID "/Voice/CallStringValid"
+
+/*
+Used if protocol support VOICE_CALL_CONTACT and VOICE_CALL_CONTACT_NEED_TEST.
+The hContact is passed as wParam and the proto should tell if this contact can be
+called.
+
+wParam: (HANDLE) hContact
+lParam: (BOOL) TRUE if it is a test for 'can call now?', FALSE if is a test for 'will be possible to call someday?'
+return: 0 if can't be called, 1 if can
+*/
+#define PS_VOICE_CALL_CONTACT_VALID "/Voice/CallContactValid"
+
+
+
+
+
+#endif // __M_VOICE_H__
diff --git a/Plugins/jingle/sdk/m_voiceservice.h b/Plugins/jingle/sdk/m_voiceservice.h new file mode 100644 index 0000000..0751e3f --- /dev/null +++ b/Plugins/jingle/sdk/m_voiceservice.h @@ -0,0 +1,65 @@ +/*
+Copyright (C) 2007 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __M_VOICESERVICE_H__
+# define __M_VOICESERVICE_H__
+
+#include "m_voice.h"
+
+/*
+This services are a mirror of the services/notifications in m_voice.h,
+with the difference that that ones are to be used by protocols, and this ones
+are to be used by plugins that can make calls to contacts in multiple protocols.
+*/
+
+
+/*
+Notifies that a voice call changed state
+
+wParam: const VOICE_CALL *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_STATE "VoiceService/State"
+
+
+
+struct VOICE_MODULE
+{
+ int cbSize; // sizeof(VOICE_MODULE)
+ char *name; // The internal name of the plugin. All PS_* serivces (except PS_VOICE_GETINFO)
+ // defined in m_voide.h need to be created based in this name. For example,
+ // PS_VOICE_CALL (/Voice/Call) need to be created as <name>/Voice/Call
+ int flags; // VOICE_* from m_voice.h
+};
+/*
+Register a new plugin that can make/receive voice calls.
+
+wParam: const VOICE_MODULE *
+lParam: ignored
+return: 0 on success
+*/
+#define MS_VOICESERVICE_REGISTER "VoiceService/Register"
+
+
+
+
+
+#endif // __M_VOICESERVICE_H__
|
