summaryrefslogtreecommitdiff
path: root/Protocols
diff options
context:
space:
mode:
authorpescuma <pescuma@c086bb3d-8645-0410-b8da-73a8550f86e7>2010-01-17 22:16:02 +0000
committerpescuma <pescuma@c086bb3d-8645-0410-b8da-73a8550f86e7>2010-01-17 22:16:02 +0000
commit9bcf44d21b3a7588a267e98573396dbf2deceb12 (patch)
treeb6b1f7c20d985faa61048b594224bcc6e0e5c722 /Protocols
parent838f03d71305a62e2372f6999d4aaf096c6a2145 (diff)
sip: start of client plugins code
git-svn-id: http://pescuma.googlecode.com/svn/trunk/Miranda@209 c086bb3d-8645-0410-b8da-73a8550f86e7
Diffstat (limited to 'Protocols')
-rw-r--r--Protocols/SIP/SIPClient.cpp701
-rw-r--r--Protocols/SIP/SIPClient.h87
-rw-r--r--Protocols/SIP/SIPProto.cpp247
-rw-r--r--Protocols/SIP/SIPProto.h39
-rw-r--r--Protocols/SIP/commons.h39
-rw-r--r--Protocols/SIP/m_sip.h98
-rw-r--r--Protocols/SIP/sip.cpp112
-rw-r--r--Protocols/SIP/sip.vcproj16
-rw-r--r--Protocols/SIP/strutils.h200
9 files changed, 1304 insertions, 235 deletions
diff --git a/Protocols/SIP/SIPClient.cpp b/Protocols/SIP/SIPClient.cpp
new file mode 100644
index 0000000..efd64ee
--- /dev/null
+++ b/Protocols/SIP/SIPClient.cpp
@@ -0,0 +1,701 @@
+/*
+Copyright (C) 2009 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"
+
+static int sttCompareClients(const SIPClient *p1, const SIPClient *p2)
+{
+ return p1 < p2;
+}
+OBJLIST<SIPClient> clients(1, &sttCompareClients);
+
+
+SIPClient::SIPClient(SIP_REGISTRATION *reg)
+{
+ hasToDestroy = false;
+ udp.transport_id = -1;
+ udp.acc_id = -1;
+ udp.port = 0;
+ tcp.transport_id = -1;
+ tcp.acc_id = -1;
+ tcp.port = 0;
+ tls.transport_id = -1;
+ tls.acc_id = -1;
+ tls.port = 0;
+
+ hNetlibUser = reg->hNetlib;
+ lstrcpynA(name, reg->name, MAX_REGS(name));
+ lstrcpyn(username, reg->username, MAX_REGS(username));
+
+ callback = reg->callback;
+ callback_param = reg->callback_param;
+
+ InitializeCriticalSection(&cs);
+}
+
+
+SIPClient::~SIPClient()
+{
+ DeleteCriticalSection(&cs);
+}
+
+
+static void CALLBACK ProcessEvents(void *param)
+{
+ for(int i = 0; i < clients.getCount(); ++i)
+ {
+ SIPClient *proto = &clients[i];
+
+ EnterCriticalSection(&proto->cs);
+
+ std::vector<SIPEvent> events(proto->events);
+ proto->events.clear();
+
+ LeaveCriticalSection(&proto->cs);
+
+ for(unsigned int i = 0; i < events.size(); ++i)
+ {
+ SIPEvent &ev = events[i];
+ switch(ev.type)
+ {
+ case SIPEvent::incoming_call:
+ proto->on_incoming_call(ev.call_id);
+ break;
+ case SIPEvent::call_state:
+ proto->on_call_state(ev.call_id, ev.call_info);
+ break;
+ case SIPEvent::call_media_state:
+ proto->on_call_media_state(ev.call_id);
+ break;
+ }
+ mir_free(ev.from);
+ mir_free(ev.text);
+ mir_free(ev.mime);
+ delete ev.messageData;
+ }
+ }
+}
+
+
+static void static_on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)
+{
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ return;
+
+ SIPClient *proto = (SIPClient *) pjsua_acc_get_user_data(acc_id);
+ if (proto == NULL)
+ return;
+
+ SIPEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.type = SIPEvent::incoming_call;
+ ev.call_id = call_id;
+ ev.call_info = info;
+
+ EnterCriticalSection(&proto->cs);
+ proto->events.push_back(ev);
+ LeaveCriticalSection(&proto->cs);
+
+ CallFunctionAsync(&ProcessEvents, NULL);
+}
+
+
+static void static_on_call_state(pjsua_call_id call_id, pjsip_event *e)
+{
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ return;
+
+ SIPClient *proto = (SIPClient *) pjsua_acc_get_user_data(info.acc_id);
+ if (proto == NULL)
+ return;
+
+ SIPEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.type = SIPEvent::call_state;
+ ev.call_id = call_id;
+ ev.call_info = info;
+
+ EnterCriticalSection(&proto->cs);
+ proto->events.push_back(ev);
+ LeaveCriticalSection(&proto->cs);
+
+ CallFunctionAsync(&ProcessEvents, NULL);
+}
+
+
+static void static_on_call_media_state(pjsua_call_id call_id)
+{
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ return;
+
+ SIPClient *proto = (SIPClient *) pjsua_acc_get_user_data(info.acc_id);
+ if (proto == NULL)
+ return;
+
+ if (!proto->on_call_media_state_sync(call_id, info))
+ return;
+
+ SIPEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.type = SIPEvent::call_media_state;
+ ev.call_id = call_id;
+ ev.call_info = info;
+
+ EnterCriticalSection(&proto->cs);
+ proto->events.push_back(ev);
+ LeaveCriticalSection(&proto->cs);
+
+ CallFunctionAsync(&ProcessEvents, NULL);
+}
+
+
+static void static_on_log(int level, const char *data, int len)
+{
+ char tmp[1024];
+ mir_snprintf(tmp, MAX_REGS(tmp), "Level %d : %*s", level, len, data);
+ OutputDebugStringA(tmp);
+}
+
+
+#define TransportName(_T_) SipToTchar(pj_cstr(pjsip_transport_get_type_name(_T_)))
+
+void SIPClient::RegisterTransport(pjsip_transport_type_e type, int port, ta *ta)
+{
+ ta->transport_id = -1;
+ ta->acc_id = -1;
+ ta->port = 0;
+
+ if (port < 0)
+ return;
+
+ pjsua_transport_config cfg;
+ pjsua_transport_config_default(&cfg);
+ cfg.port = port;
+
+ pj_status_t status = pjsua_transport_create(type, &cfg, &ta->transport_id);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error creating %s transport"), (const TCHAR *) TransportName(type));
+ return;
+ }
+
+ pjsua_transport_info info;
+ status = pjsua_transport_get_info(ta->transport_id, &info);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error getting %s info"), (const TCHAR *) TransportName(type));
+ pjsua_transport_close(ta->transport_id, PJ_TRUE);
+ ta->transport_id = -1;
+ return;
+ }
+
+ status = pjsua_acc_add_local(ta->transport_id, PJ_TRUE, &ta->acc_id);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error adding %s account"), (const TCHAR *) TransportName(type));
+ pjsua_transport_close(ta->transport_id, PJ_TRUE);
+ ta->transport_id = -1;
+ return;
+ }
+
+ lstrcpyn(host, SipToTchar(info.local_name.host), MAX_REGS(host));
+ ta->port = info.local_name.port;
+}
+
+
+int SIPClient::Connect(int udp_port, int tcp_port, int tls_port)
+{
+ Trace(_T("Connecting..."));
+
+ {
+ pj_status_t status = pjsua_create();
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error creating pjsua"));
+ return 1;
+ }
+ hasToDestroy = true;
+ }
+
+ {
+ pjsua_config cfg;
+ pjsua_config_default(&cfg);
+#ifndef _DEBUG
+ cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
+#endif
+ cfg.cb.on_incoming_call = &static_on_incoming_call;
+ cfg.cb.on_call_media_state = &static_on_call_media_state;
+ cfg.cb.on_call_state = &static_on_call_state;
+
+ pjsua_logging_config log_cfg;
+ pjsua_logging_config_default(&log_cfg);
+ log_cfg.cb = &static_on_log;
+
+ pjsua_media_config media_cfg;
+ pjsua_media_config_default(&media_cfg);
+ media_cfg.enable_ice = PJ_TRUE;
+
+ pj_status_t status = pjsua_init(&cfg, &log_cfg, &media_cfg);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error initializing pjsua"));
+ return 1;
+ }
+ }
+
+ {
+ RegisterTransport(PJSIP_TRANSPORT_UDP, udp_port, &udp);
+ RegisterTransport(PJSIP_TRANSPORT_TCP, tcp_port, &tcp);
+ RegisterTransport(PJSIP_TRANSPORT_TLS, tls_port, &tls);
+
+ if (udp.port <= 0 && tcp.port <= 0 && tls.port <= 0)
+ return 1;
+ }
+
+ {
+ pj_status_t status = pjsua_start();
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error starting pjsua"));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+#define MESSAGE_TYPE_TRACE 0
+#define MESSAGE_TYPE_INFO 1
+#define MESSAGE_TYPE_ERROR 2
+
+
+void SIPClient::Trace(TCHAR *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ ShowMessage(MESSAGE_TYPE_TRACE, fmt, args);
+
+ va_end(args);
+}
+
+
+void SIPClient::Info(TCHAR *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ ShowMessage(MESSAGE_TYPE_INFO, TranslateTS(fmt), args);
+
+ va_end(args);
+}
+
+
+void SIPClient::Error(TCHAR *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ ShowMessage(MESSAGE_TYPE_ERROR, TranslateTS(fmt), args);
+
+ va_end(args);
+}
+
+
+void SIPClient::Error(pj_status_t status, TCHAR *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ TCHAR buff[1024];
+ mir_vsntprintf(buff, MAX_REGS(buff), TranslateTS(fmt), args);
+
+ va_end(args);
+
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+
+ Error(_T("%s: %s"), buff, Utf8ToTchar(errmsg));
+}
+
+
+void SIPClient::ShowMessage(int type, TCHAR *fmt, va_list args)
+{
+ TCHAR buff[1024];
+ if (type == MESSAGE_TYPE_TRACE)
+ lstrcpy(buff, _T("SIP: TRACE: "));
+ else if (type == MESSAGE_TYPE_INFO)
+ lstrcpy(buff, _T("SIP: INFO : "));
+ else
+ lstrcpy(buff, _T("SIP: ERROR: "));
+
+ mir_vsntprintf(&buff[12], MAX_REGS(buff)-12, fmt, args);
+
+#ifdef _DEBUG
+ OutputDebugString(buff);
+ OutputDebugString(_T("\n"));
+#endif
+
+ if (hNetlibUser)
+ CallService(MS_NETLIB_LOG, (WPARAM) hNetlibUser, (LPARAM) TcharToChar(buff).get());
+}
+
+
+void SIPClient::Disconnect()
+{
+ Trace(_T("Disconnecting..."));
+
+ EnterCriticalSection(&cs);
+ events.clear();
+ LeaveCriticalSection(&cs);
+
+#define CLOSE(_TA_) \
+ if (_TA_.acc_id >= 0) \
+ { \
+ pjsua_acc_del(_TA_.acc_id); \
+ _TA_.acc_id = -1; \
+ } \
+ if (_TA_.transport_id >= 0) \
+ { \
+ pjsua_transport_close(_TA_.transport_id, PJ_TRUE); \
+ _TA_.transport_id = -1; \
+ }
+
+ CLOSE(udp)
+ CLOSE(tcp)
+ CLOSE(tls)
+
+ if (hasToDestroy)
+ {
+ pjsua_destroy();
+ hasToDestroy = false;
+ }
+}
+
+
+void SIPClient::NotifyCall(pjsua_call_id call_id, int state, const TCHAR *name, const TCHAR *uri)
+{
+ Trace(_T("NotifyCall %d -> %d"), call_id, state);
+
+ if (callback != NULL)
+ callback(callback_param, (int) call_id, state, name, uri);
+}
+
+
+void SIPClient::on_incoming_call(pjsua_call_id call_id)
+{
+ Trace(_T("on_incoming_call: %d"), call_id);
+
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error obtaining call info"));
+ return;
+ }
+
+ SipToTchar remote_info(info.remote_info);
+ SipToTchar remote_contact(info.remote_contact);
+
+ TCHAR name[256];
+ CleanupURI(name, MAX_REGS(name), remote_info);
+
+ NotifyCall(call_id, VOICE_STATE_RINGING, name, remote_contact);
+}
+
+
+void SIPClient::on_call_state(pjsua_call_id call_id, const pjsua_call_info &info)
+{
+ Trace(_T("on_call_state: %d"), call_id);
+ Trace(_T("Call info: %d / last: %d %s"), info.state, info.last_status, info.last_status_text);
+
+ switch(info.state)
+ {
+ case PJSIP_INV_STATE_NULL:
+ case PJSIP_INV_STATE_DISCONNECTED:
+ {
+ NotifyCall(call_id, VOICE_STATE_ENDED);
+ break;
+ }
+ case PJSIP_INV_STATE_CONFIRMED:
+ {
+ NotifyCall(call_id, VOICE_STATE_TALKING);
+ break;
+ }
+ }
+}
+
+
+bool SIPClient::on_call_media_state_sync(pjsua_call_id call_id, const pjsua_call_info &info)
+{
+ Trace(_T("on_call_media_state_sync: %d"), call_id);
+
+ // Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
+ // otherwise we should NOT connect the ports.
+ if (info.media_status == PJSUA_CALL_MEDIA_ACTIVE || info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
+ {
+ ConfigureDevices();
+
+ pjsua_conf_connect(info.conf_slot, 0);
+ pjsua_conf_connect(0, info.conf_slot);
+ }
+
+ return true;
+}
+
+
+void SIPClient::on_call_media_state(pjsua_call_id call_id)
+{
+ Trace(_T("on_call_media_state: %d"), call_id);
+
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error obtaining call info"));
+ return;
+ }
+
+ if (info.media_status == PJSUA_CALL_MEDIA_ACTIVE)
+ {
+ NotifyCall(call_id, VOICE_STATE_TALKING);
+ }
+ else if (info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD)
+ {
+ NotifyCall(call_id, VOICE_STATE_ON_HOLD);
+ }
+}
+
+
+static int GetDevice(bool out, int defaultVal)
+{
+ DBVARIANT dbv;
+ if (DBGetContactSettingString(NULL, "VoiceService", out ? "Output" : "Input", &dbv))
+ return defaultVal;
+
+ int ret = defaultVal;
+
+ unsigned count = pjmedia_aud_dev_count();
+ for (unsigned i = 0; i < count; ++i)
+ {
+ pjmedia_aud_dev_info info;
+
+ pj_status_t status = pjmedia_aud_dev_get_info(i, &info);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ if (!out && info.input_count < 1)
+ continue;
+ if (out && info.output_count < 1)
+ continue;
+
+ if (strcmp(dbv.pszVal, info.name) == 0)
+ {
+ ret = i;
+ break;
+ }
+ }
+
+ DBFreeVariant(&dbv);
+ return ret;
+}
+
+
+void SIPClient::ConfigureDevices()
+{
+ int input, output;
+ pjsua_get_snd_dev(&input, &output);
+
+ int expectedInput = GetDevice(false, input);
+ int expectedOutput = GetDevice(true, output);
+
+ if (input != expectedInput || output != expectedOutput)
+ pjsua_set_snd_dev(input, output);
+
+ if (DBGetContactSettingByte(NULL, "VoiceService", "EchoCancelation", TRUE))
+ pjsua_set_ec(PJSUA_DEFAULT_EC_TAIL_LEN, 0);
+ else
+ pjsua_set_ec(0, 0);
+}
+
+
+void SIPClient::CleanupURI(TCHAR *out, int outSize, const TCHAR *url)
+{
+ if (url[0] == _T('"'))
+ {
+ const TCHAR *other = _tcsstr(&url[1], _T("\" <"));
+ if (other != NULL)
+ url = other + 2;
+ }
+
+ lstrcpyn(out, url, outSize);
+
+ RemoveLtGt(out);
+
+ TCHAR *info = _tcschr(out, _T(';'));
+ if (info != NULL)
+ *info = 0;
+
+ RemoveLtGt(out);
+
+ info = _tcschr(out, _T(';'));
+ if (info != NULL)
+ *info = 0;
+
+ if (_tcsnicmp(_T("sip:"), out, 4) == 0)
+ lstrcpyn(out, &out[4], outSize);
+}
+
+
+void SIPClient::BuildURI(TCHAR *out, int outSize, const TCHAR *user, const TCHAR *host, int port, int protocol)
+{
+ if (protocol == PJSIP_TRANSPORT_UDP)
+ mir_sntprintf(out, outSize, _T("<sip:%s@%s:%d>"), user, host, port);
+ else
+ mir_sntprintf(out, outSize, _T("<sip:%s@%s:%d;transport=%s>"), user, host,
+ (const TCHAR *) TransportName((pjsip_transport_type_e) protocol));
+}
+
+pjsua_call_id SIPClient::Call(const TCHAR *username, const TCHAR *host, int port, int protocol)
+{
+ pjsua_acc_id acc_id;
+ switch(protocol)
+ {
+ case PJSIP_TRANSPORT_UDP: acc_id = udp.acc_id; break;
+ case PJSIP_TRANSPORT_TCP: acc_id = tcp.acc_id; break;
+ case PJSIP_TRANSPORT_TLS: acc_id = tls.acc_id; break;
+ default: return -1;
+ }
+ if (acc_id < 0)
+ return -1;
+
+ TCHAR uri[1024];
+ BuildURI(uri, MAX_REGS(uri), username, host, port, protocol);
+
+ pjsua_call_id call_id;
+ pj_str_t ret;
+ pj_status_t status = pjsua_call_make_call(acc_id, pj_cstr(&ret, TcharToSip(uri)), 0, NULL, NULL, &call_id);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error making call"));
+ return -1;
+ }
+
+ NotifyCall(call_id, VOICE_STATE_CALLING, username, uri);
+
+ return call_id;
+}
+
+int SIPClient::DropCall(pjsua_call_id call_id)
+{
+ if (call_id < 0 || call_id >= (int) pjsua_call_get_max_count())
+ return 2;
+
+ pj_status_t status = pjsua_call_hangup(call_id, 0, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error hanging up call"));
+ return -1;
+ }
+
+ return 0;
+}
+
+int SIPClient::HoldCall(pjsua_call_id call_id)
+{
+ if (call_id < 0 || call_id >= (int) pjsua_call_get_max_count())
+ return 2;
+
+ pj_status_t status = pjsua_call_set_hold(call_id, NULL);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error holding call"));
+ return -1;
+ }
+
+ return 0;
+}
+
+int SIPClient::AnswerCall(pjsua_call_id call_id)
+{
+ if (call_id < 0 || call_id >= (int) pjsua_call_get_max_count())
+ return 2;
+
+ pjsua_call_info info;
+ pj_status_t status = pjsua_call_get_info(call_id, &info);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error obtaining call info"));
+ return -1;
+ }
+
+ if (info.state == PJSIP_INV_STATE_INCOMING)
+ {
+ pj_status_t status = pjsua_call_answer(call_id, 200, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error answering call"));
+ return -1;
+ }
+ }
+ else if (info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD)
+ {
+ pj_status_t status = pjsua_call_reinvite(call_id, PJ_TRUE, NULL);
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error un-holding call"));
+ return -1;
+ }
+ }
+ else
+ {
+ // Wrong state
+ return 3;
+ }
+
+ return 0;
+}
+
+int SIPClient::SendDTMF(pjsua_call_id call_id, TCHAR dtmf)
+{
+ if (call_id < 0 || call_id >= (int) pjsua_call_get_max_count())
+ return 2;
+
+ if (dtmf >= _T('a') && dtmf <= _T('d'))
+ dtmf += _T('A') - _T('a');
+
+ if (!IsValidDTMF(dtmf))
+ return 3;
+
+ TCHAR tmp[2];
+ tmp[0] = dtmf;
+ tmp[1] = 0;
+
+ pj_str_t ret;
+ pjsua_call_dial_dtmf(call_id, pj_cstr(&ret, TcharToSip(tmp)));
+
+ return 0;
+}
diff --git a/Protocols/SIP/SIPClient.h b/Protocols/SIP/SIPClient.h
new file mode 100644
index 0000000..275d032
--- /dev/null
+++ b/Protocols/SIP/SIPClient.h
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 2009 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 __SIPCLIENT_H__
+#define __SIPCLIENT_H__
+
+
+class SIPClient
+{
+private:
+ HANDLE hNetlibUser;
+ bool hasToDestroy;
+
+public:
+ struct ta {
+ pjsua_transport_id transport_id;
+ pjsua_acc_id acc_id;
+ int port;
+ };
+
+ ta udp;
+ ta tcp;
+ ta tls;
+
+ SIPClientCallback callback;
+ void *callback_param;
+
+ char name[512];
+ TCHAR username[16];
+ TCHAR host[256];
+
+ CRITICAL_SECTION cs;
+ std::vector<SIPEvent> events;
+
+ SIPClient(SIP_REGISTRATION *reg);
+ virtual ~SIPClient();
+
+ void on_incoming_call(pjsua_call_id call_id);
+ void on_call_state(pjsua_call_id call_id, const pjsua_call_info &info);
+ bool on_call_media_state_sync(pjsua_call_id call_id, const pjsua_call_info &info);
+ void on_call_media_state(pjsua_call_id call_id);
+
+ pjsua_call_id Call(const TCHAR *username, const TCHAR *host, int port, int protocol);
+ int DropCall(pjsua_call_id call_id);
+ int HoldCall(pjsua_call_id call_id);
+ int AnswerCall(pjsua_call_id call_id);
+ int SendDTMF(pjsua_call_id call_id, TCHAR dtmf);
+
+ int Connect(int udp_port, int tcp_port, int tls_port);
+ void Disconnect();
+
+private:
+ void Trace(TCHAR *fmt, ...);
+ void Info(TCHAR *fmt, ...);
+ void Error(TCHAR *fmt, ...);
+ void Error(pj_status_t status, TCHAR *fmt, ...);
+ void ShowMessage(int type, TCHAR *fmt, va_list args);
+
+ void RegisterTransport(pjsip_transport_type_e type, int port, ta *ta);
+
+ void ConfigureDevices();
+ void BuildURI(TCHAR *out, int outSize, const TCHAR *user, const TCHAR *host, int port, int protocol);
+ void CleanupURI(TCHAR *out, int outSize, const TCHAR *url);
+
+ void NotifyCall(pjsua_call_id call_id, int state, const TCHAR *name = NULL, const TCHAR *uri = NULL);
+};
+
+
+
+
+#endif // __SIPCLIENT_H__
diff --git a/Protocols/SIP/SIPProto.cpp b/Protocols/SIP/SIPProto.cpp
index 7d5e84d..9a1c949 100644
--- a/Protocols/SIP/SIPProto.cpp
+++ b/Protocols/SIP/SIPProto.cpp
@@ -26,140 +26,12 @@ static INT_PTR CALLBACK DlgProcAccMgrUI(HWND hwndDlg, UINT msg, WPARAM wParam, L
static INT_PTR CALLBACK DlgOptions(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-#define TcharToSip TcharToUtf8
-
-class SipToTchar
-{
-public:
- SipToTchar(const pj_str_t &str) : tchar(NULL)
- {
- if (str.ptr == NULL)
- return;
-
- int size = MultiByteToWideChar(CP_UTF8, 0, str.ptr, str.slen, NULL, 0);
- if (size < 0)
- throw _T("Could not convert string to WCHAR");
- size++;
-
- WCHAR *tmp = (WCHAR *) mir_alloc(size * sizeof(WCHAR));
- if (tmp == NULL)
- throw _T("mir_alloc returned NULL");
-
- MultiByteToWideChar(CP_UTF8, 0, str.ptr, str.slen, tmp, size);
- tmp[size-1] = 0;
-
-#ifdef UNICODE
-
- tchar = tmp;
-
-#else
-
- size = WideCharToMultiByte(CP_ACP, 0, tmp, -1, NULL, 0, NULL, NULL);
- if (size <= 0)
- {
- mir_free(tmp);
- throw _T("Could not convert string to ACP");
- }
-
- tchar = (TCHAR *) mir_alloc(size * sizeof(char));
- if (tchar == NULL)
- {
- mir_free(tmp);
- throw _T("mir_alloc returned NULL");
- }
-
- WideCharToMultiByte(CP_ACP, 0, tmp, -1, tchar, size, NULL, NULL);
-
- mir_free(tmp);
-
-#endif
- }
-
- ~SipToTchar()
- {
- if (tchar != NULL)
- mir_free(tchar);
- }
-
- TCHAR *detach()
- {
- TCHAR *ret = tchar;
- tchar = NULL;
- return ret;
- }
-
- TCHAR * get() const
- {
- return tchar;
- }
-
- operator TCHAR *() const
- {
- return tchar;
- }
-
- const TCHAR & operator[](int pos) const
- {
- return tchar[pos];
- }
-
-private:
- TCHAR *tchar;
-};
-
-
-static pj_str_t pj_str(const char *str)
-{
- pj_str_t ret;
- pj_cstr(&ret, str);
- return ret;
-}
-
-
-static char * mir_pjstrdup(const pj_str_t *from)
-{
- char *ret = (char *) mir_alloc(from->slen + 1);
- strncpy(ret, from->ptr, from->slen);
- ret[from->slen] = 0;
- return ret;
-}
-
-
-static int FirstGtZero(int n1, int n2)
-{
- if (n1 > 0)
- return n1;
- return n2;
-}
-
-
-static TCHAR *CleanupSip(TCHAR *str)
-{
- if (_tcsnicmp(_T("sip:"), str, 4) == 0)
- return &str[4];
- else
- return str;
-}
-
-
-static const TCHAR *CleanupSip(const TCHAR *str)
-{
- if (_tcsnicmp(_T("sip:"), str, 4) == 0)
- return &str[4];
- else
- return str;
-}
-
-
-
-
-
SIPProto::SIPProto(const char *aProtoName, const TCHAR *aUserName)
{
- InitializeCriticalSection(&cs);
-
hasToDestroy = false;
- transport_id = -1;
+ udp_transport_id = -1;
+ tcp_transport_id = -1;
+ tls_transport_id = -1;
acc_id = -1;
hNetlibUser = 0;
hCallStateEvent = 0;
@@ -169,6 +41,18 @@ SIPProto::SIPProto(const char *aProtoName, const TCHAR *aUserName)
memset(awayMessages, 0, sizeof(awayMessages));
+ {
+ pj_status_t status = pjsua_create();
+ if (status != PJ_SUCCESS)
+ {
+ Error(status, _T("Error creating pjsua"));
+ throw "Error creating pjsua";
+ }
+ hasToDestroy = true;
+ }
+
+ InitializeCriticalSection(&cs);
+
m_tszUserName = mir_tstrdup(aUserName);
m_szProtoName = mir_strdup(aProtoName);
m_szModuleName = mir_strdup(aProtoName);
@@ -625,22 +509,14 @@ int SIPProto::Connect()
BroadcastStatus(ID_STATUS_CONNECTING);
{
- pj_status_t status = pjsua_create();
- if (status != PJ_SUCCESS)
- {
- Error(status, _T("Error creating pjsua"));
- return -1;
- }
- hasToDestroy = true;
- }
-
- {
scoped_mir_free<char> stun;
scoped_mir_free<char> dns;
pjsua_config cfg;
pjsua_config_default(&cfg);
- //cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
+#ifndef DEBUG
+ cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
+#endif
cfg.cb.on_incoming_call = &static_on_incoming_call;
cfg.cb.on_call_media_state = &static_on_call_media_state;
cfg.cb.on_call_state = &static_on_call_state;
@@ -681,7 +557,11 @@ int SIPProto::Connect()
pjsua_logging_config_default(&log_cfg);
log_cfg.cb = &static_on_log;
- pj_status_t status = pjsua_init(&cfg, &log_cfg, NULL);
+ pjsua_media_config media_cfg;
+ pjsua_media_config_default(&media_cfg);
+ media_cfg.enable_ice = PJ_TRUE;
+
+ pj_status_t status = pjsua_init(&cfg, &log_cfg, &media_cfg);
if (status != PJ_SUCCESS)
{
Error(status, _T("Error initializing pjsua"));
@@ -693,13 +573,21 @@ int SIPProto::Connect()
{
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);
- pj_status_t status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &transport_id);
+ pj_status_t status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &udp_transport_id);
if (status != PJ_SUCCESS)
{
- Error(status, _T("Error creating transport"));
+ Error(status, _T("Error creating UDP transport"));
Disconnect();
return 2;
}
+
+ status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, &tcp_transport_id);
+ if (status != PJ_SUCCESS)
+ Error(status, _T("Error creating TCP transport"));
+
+ status = pjsua_transport_create(PJSIP_TRANSPORT_TLS, &cfg, &udp_transport_id);
+ if (status != PJ_SUCCESS)
+ Error(status, _T("Error creating TLS transport"));
}
{
@@ -720,8 +608,9 @@ int SIPProto::Connect()
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
cfg.user_data = this;
- cfg.transport_id = transport_id;
- //cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
+#ifndef DEBUG
+ cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
+#endif
BuildURI(tmp, MAX_REGS(tmp), opts.username, opts.domain);
TcharToSip id(tmp);
@@ -1018,17 +907,16 @@ void SIPProto::Disconnect()
acc_id = -1;
}
- if (transport_id >= 0)
- {
- pjsua_transport_close(transport_id, PJ_FALSE);
- transport_id = -1;
+#define CLOSE_TRANSPORT(_T_) \
+ if (_T_ >= 0) \
+ { \
+ pjsua_transport_close(_T_, PJ_FALSE); \
+ _T_ = -1; \
}
- if (hasToDestroy)
- {
- pjsua_destroy();
- hasToDestroy = false;
- }
+ CLOSE_TRANSPORT(udp_transport_id)
+ CLOSE_TRANSPORT(tcp_transport_id)
+ CLOSE_TRANSPORT(tls_transport_id)
for(HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
hContact != NULL;
@@ -1108,6 +996,13 @@ int __cdecl SIPProto::OnOptionsInit(WPARAM wParam, LPARAM lParam)
int __cdecl SIPProto::OnPreShutdown(WPARAM wParam, LPARAM lParam)
{
+ if (hasToDestroy)
+ {
+ pjsua_destroy();
+ hasToDestroy = false;
+ }
+
+
return 0;
}
@@ -1209,16 +1104,6 @@ void SIPProto::on_reg_state()
}
}
-static void RemoveLtGt(TCHAR *str)
-{
- int len = lstrlen(str);
- if (str[0] == _T('<') && str[len-1] == _T('>'))
- {
- str[len-1] = 0;
- memmove(str, &str[1], len * sizeof(TCHAR));
- }
-}
-
void SIPProto::on_incoming_call(pjsua_call_id call_id)
{
Trace(_T("on_incoming_call: %d"), call_id);
@@ -1382,23 +1267,6 @@ int __cdecl SIPProto::VoiceCaps(WPARAM wParam,LPARAM lParam)
}
-static void CleanupNumber(TCHAR *out, int outSize, const TCHAR *number)
-{
- int pos = 0;
- int len = lstrlen(number);
- for(int i = 0; i < len && pos < outSize - 1; ++i)
- {
- TCHAR c = number[i];
-
- if (i == 0 && c == _T('+'))
- out[pos++] = c;
- else if (c >= _T('0') && c <= _T('9'))
- out[pos++] = c;
- }
- out[pos] = 0;
-}
-
-
void SIPProto::CleanupURI(TCHAR *out, int outSize, const TCHAR *url)
{
if (url[0] == _T('"'))
@@ -1613,19 +1481,6 @@ int __cdecl SIPProto::VoiceHoldCall(WPARAM wParam, LPARAM lParam)
}
-static bool IsValidDTMF(TCHAR c)
-{
- if (c >= _T('A') && c <= _T('D'))
- return true;
- if (c >= _T('0') && c <= _T('9'))
- return true;
- if (c == _T('#') || c == _T('*'))
- return true;
-
- return false;
-}
-
-
int __cdecl SIPProto::VoiceSendDTMF(WPARAM wParam, LPARAM lParam)
{
char *id = (char *) wParam;
diff --git a/Protocols/SIP/SIPProto.h b/Protocols/SIP/SIPProto.h
index 5c449de..5d3797f 100644
--- a/Protocols/SIP/SIPProto.h
+++ b/Protocols/SIP/SIPProto.h
@@ -27,38 +27,6 @@ typedef INT_PTR (__cdecl SIPProto::*SIPServiceFunc)(WPARAM, LPARAM);
typedef INT_PTR (__cdecl SIPProto::*SIPServiceFuncParam)(WPARAM, LPARAM, LPARAM);
typedef int (__cdecl SIPProto::*SIPEventFunc)(WPARAM, LPARAM);
-struct MessageData
-{
- HANDLE hContact;
- LONG messageID;
- pjsip_status_code status;
-};
-
-struct SIPEvent
-{
- enum {
- reg_state,
- incoming_call,
- call_state,
- call_media_state,
- incoming_subscribe,
- buddy_state,
- pager,
- pager_status,
- typing
-
- } type;
-
- pjsua_call_id call_id;
- pjsua_call_info call_info;
- pjsua_buddy_id buddy_id;
- char *from;
- char *text;
- char *mime;
- bool isTyping;
- MessageData *messageData;
- pjsua_srv_pres *srv_pres;
-};
class SIPProto : public PROTO_INTERFACE
{
@@ -66,7 +34,9 @@ private:
HANDLE hNetlibUser;
HANDLE hCallStateEvent;
bool hasToDestroy;
- pjsua_transport_id transport_id;
+ pjsua_transport_id udp_transport_id;
+ pjsua_transport_id tcp_transport_id;
+ pjsua_transport_id tls_transport_id;
pjsua_acc_id acc_id;
LONG messageID;
LONG awayMessageID;
@@ -228,9 +198,6 @@ private:
void __cdecl GetAwayMsgThread(void* arg);
TCHAR *GetAwayMsg(int status);
INT_PTR __cdecl GetMyAwayMsg(WPARAM wParam, LPARAM lParam);
-
- // Static callbacks
- static void CALLBACK DisconnectProto(void *param);
};
diff --git a/Protocols/SIP/commons.h b/Protocols/SIP/commons.h
index 47b4cfd..1fb4108 100644
--- a/Protocols/SIP/commons.h
+++ b/Protocols/SIP/commons.h
@@ -80,7 +80,46 @@ Boston, MA 02111-1307, USA.
#include "../../plugins/voiceservice/m_voiceservice.h"
#include "resource.h"
+#include "strutils.h"
+
+
+struct MessageData
+{
+ HANDLE hContact;
+ LONG messageID;
+ pjsip_status_code status;
+};
+
+struct SIPEvent
+{
+ enum {
+ reg_state,
+ incoming_call,
+ call_state,
+ call_media_state,
+ incoming_subscribe,
+ buddy_state,
+ pager,
+ pager_status,
+ typing
+
+ } type;
+
+ pjsua_call_id call_id;
+ pjsua_call_info call_info;
+ pjsua_buddy_id buddy_id;
+ char *from;
+ char *text;
+ char *mime;
+ bool isTyping;
+ MessageData *messageData;
+ pjsua_srv_pres *srv_pres;
+};
+
+
+#include "m_sip.h"
#include "SIPProto.h"
+#include "SIPClient.h"
#define MODULE_NAME "SIP"
diff --git a/Protocols/SIP/m_sip.h b/Protocols/SIP/m_sip.h
new file mode 100644
index 0000000..22b9e03
--- /dev/null
+++ b/Protocols/SIP/m_sip.h
@@ -0,0 +1,98 @@
+/*
+Copyright (C) 2010 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_SIP_H__
+# define __M_SIP_H__
+
+
+// state is a VOICE_STATE_*
+typedef void (*SIPClientCallback)(void *param, int callId, int state, const TCHAR *name, const TCHAR *uri);
+
+struct SIP_REGISTRATION
+{
+ int cbSize;
+ const char *name; // Internal name of client
+ const TCHAR *username;
+ int udp_port; // UDP port to be used: 0 means random, -1 means don't want UDP
+ int tcp_port; // UDP port to be used: 0 means TCP, -1 means don't want TCP
+ int tls_port; // UDP port to be used: 0 means TLS, -1 means don't want TLS
+
+ HANDLE hNetlib; // To be used for logs. Can be 0
+
+ SIPClientCallback callback;
+ void *callback_param;
+};
+
+
+struct SIP_CLIENT
+{
+ void *data; // Do not touch
+ const TCHAR *username;
+ const TCHAR *host;
+ const int udp_port;
+ const int tcp_port;
+ const int tls_port;
+
+ // @param protocol 1 UDP, 2 TCP, 3 TLS
+ // @return callId or <0 on error
+ int (*Call)(SIP_CLIENT *sip, const TCHAR *username, const TCHAR *host, int port, int protocol);
+
+ // @return 0 on success
+ int (*DropCall)(SIP_CLIENT *sip, int callId);
+
+ // @return 0 on success
+ int (*HoldCall)(SIP_CLIENT *sip, int callId);
+
+ // @return 0 on success
+ int (*AnswerCall)(SIP_CLIENT *sip, int callId);
+
+ // @return 0 on success
+ int (*SendDTMF)(SIP_CLIENT *sip, int callId, TCHAR dtmf);
+};
+
+
+/*
+Register a SIP client, allowing it to make calls
+
+wParam = SIP_REGISTRATION *
+lParam = 0
+return SIP_CLIENT * or NULL on error
+*/
+#define MS_SIP_REGISTER "SIP/Client/Register"
+
+
+/*
+Unregister a SIP client and free the internal structures.
+
+wParam = SIP_CLIENT *
+lParam = 0
+return 0 on success
+*/
+#define MS_SIP_UNREGISTER "SIP/Client/Unregister"
+
+
+
+
+
+
+
+
+
+#endif // __M_SIP_H__
diff --git a/Protocols/SIP/sip.cpp b/Protocols/SIP/sip.cpp
index cb3dc36..4bfaeff 100644
--- a/Protocols/SIP/sip.cpp
+++ b/Protocols/SIP/sip.cpp
@@ -57,6 +57,9 @@ std::vector<HANDLE> hHooks;
int ModulesLoaded(WPARAM wParam, LPARAM lParam);
int PreShutdown(WPARAM wParam, LPARAM lParam);
+static INT_PTR ClientRegister(WPARAM wParam, LPARAM lParam);
+static INT_PTR ClientUnregister(WPARAM wParam, LPARAM lParam);
+
static int sttCompareProtocols(const SIPProto *p1, const SIPProto *p2)
{
@@ -106,9 +109,16 @@ static SIPProto *SIPProtoInit(const char* pszProtoName, const TCHAR* tszUserName
return NULL;
}
- SIPProto *proto = new SIPProto(pszProtoName, tszUserName);
- instances.insert(proto);
- return proto;
+ try
+ {
+ SIPProto *proto = new SIPProto(pszProtoName, tszUserName);
+ instances.insert(proto);
+ return proto;
+ }
+ catch(const char *)
+ {
+ return NULL;
+ }
}
@@ -192,6 +202,9 @@ extern "C" int __declspec(dllexport) Load(PLUGINLINK *link)
pd.type = PROTOTYPE_PROTOCOL;
CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM) &pd);
+ CreateServiceFunction(MS_SIP_REGISTER, ClientRegister);
+ CreateServiceFunction(MS_SIP_UNREGISTER, ClientUnregister);
+
return 0;
}
@@ -248,3 +261,96 @@ int PreShutdown(WPARAM wParam, LPARAM lParam)
return 0;
}
+
+
+static int ClientCall(SIP_CLIENT *sip, const TCHAR *username, const TCHAR *host, int port, int protocol)
+{
+ if (sip == NULL || sip->data == NULL)
+ return -1;
+
+ SIPClient *cli = (SIPClient *) sip->data;
+ return (int) cli->Call(username, host, port, protocol);
+}
+
+static int ClientDropCall(SIP_CLIENT *sip, int callId)
+{
+ if (sip == NULL || sip->data == NULL)
+ return -1;
+
+ SIPClient *cli = (SIPClient *) sip->data;
+ return (int) cli->DropCall((pjsua_call_id) callId);
+}
+
+static int ClientHoldCall(SIP_CLIENT *sip, int callId)
+{
+ if (sip == NULL || sip->data == NULL)
+ return -1;
+
+ SIPClient *cli = (SIPClient *) sip->data;
+ return (int) cli->HoldCall((pjsua_call_id) callId);
+}
+
+static int ClientAnswerCall(SIP_CLIENT *sip, int callId)
+{
+ if (sip == NULL || sip->data == NULL)
+ return -1;
+
+ SIPClient *cli = (SIPClient *) sip->data;
+ return (int) cli->AnswerCall((pjsua_call_id) callId);
+}
+
+static int ClientSendDTMF(SIP_CLIENT *sip, int callId, TCHAR dtmf)
+{
+ if (sip == NULL || sip->data == NULL)
+ return -1;
+
+ SIPClient *cli = (SIPClient *) sip->data;
+ return (int) cli->SendDTMF((pjsua_call_id) callId, dtmf);
+}
+
+static INT_PTR ClientRegister(WPARAM wParam, LPARAM lParam)
+{
+ SIP_REGISTRATION *reg = (SIP_REGISTRATION *) wParam;
+ if (reg == NULL || reg->cbSize < sizeof(SIP_REGISTRATION))
+ return NULL;
+
+ if (reg->name[0] == 0 || reg->username[0] == 0)
+ return NULL;
+
+ SIPClient *cli = new SIPClient(reg);
+ if (cli->Connect(reg->udp_port, reg->tcp_port, reg->tls_port) != 0)
+ {
+ cli->Disconnect();
+ delete cli;
+ return NULL;
+ }
+
+ SIP_CLIENT *ret = (SIP_CLIENT *) mir_alloc0(sizeof(SIP_CLIENT) + sizeof(SIPClient *));
+ ret->data = cli;
+ * (TCHAR **) & ret->username = cli->username;
+ * (TCHAR **) & ret->host = cli->host;
+ * (int *) & ret->udp_port = cli->udp.port;
+ * (int *) & ret->tcp_port = cli->tcp.port;
+ * (int *) & ret->tls_port = cli->tls.port;
+ ret->Call = &ClientCall;
+ ret->DropCall = &ClientDropCall;
+ ret->HoldCall = &ClientHoldCall;
+ ret->AnswerCall = &ClientAnswerCall;
+ ret->SendDTMF = &ClientSendDTMF;
+
+ return (INT_PTR) ret;
+}
+
+static INT_PTR ClientUnregister(WPARAM wParam, LPARAM lParam)
+{
+ SIP_CLIENT *sc = (SIP_CLIENT *) wParam;
+ if (sc == NULL || sc->data == NULL)
+ return 1;
+
+ SIPClient * cli = (SIPClient *) sc->data;
+ cli->Disconnect();
+ delete cli;
+ mir_free(sc);
+
+ return 0;
+}
diff --git a/Protocols/SIP/sip.vcproj b/Protocols/SIP/sip.vcproj
index 1d867aa..ddb8396 100644
--- a/Protocols/SIP/sip.vcproj
+++ b/Protocols/SIP/sip.vcproj
@@ -425,6 +425,10 @@
>
</File>
<File
+ RelativePath=".\m_sip.h"
+ >
+ </File>
+ <File
RelativePath="..\..\plugins\utils\mir_icons.h"
>
</File>
@@ -445,9 +449,17 @@
>
</File>
<File
+ RelativePath=".\SIPClient.h"
+ >
+ </File>
+ <File
RelativePath="SIPProto.h"
>
</File>
+ <File
+ RelativePath=".\strutils.h"
+ >
+ </File>
</Filter>
<Filter
Name="Resource Files"
@@ -655,6 +667,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath=".\SIPClient.cpp"
+ >
+ </File>
+ <File
RelativePath="SIPProto.cpp"
>
<FileConfiguration
diff --git a/Protocols/SIP/strutils.h b/Protocols/SIP/strutils.h
new file mode 100644
index 0000000..b534ac2
--- /dev/null
+++ b/Protocols/SIP/strutils.h
@@ -0,0 +1,200 @@
+/*
+Copyright (C) 2009 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.
+*/
+
+#pragma once
+#ifndef __STRUTILS_H__
+#define __STRUTILS_H__
+
+
+#define TcharToSip TcharToUtf8
+
+class SipToTchar
+{
+public:
+ SipToTchar(const pj_str_t &str) : tchar(NULL)
+ {
+ if (str.ptr == NULL)
+ return;
+
+ int size = MultiByteToWideChar(CP_UTF8, 0, str.ptr, str.slen, NULL, 0);
+ if (size < 0)
+ throw _T("Could not convert string to WCHAR");
+ size++;
+
+ WCHAR *tmp = (WCHAR *) mir_alloc(size * sizeof(WCHAR));
+ if (tmp == NULL)
+ throw _T("mir_alloc returned NULL");
+
+ MultiByteToWideChar(CP_UTF8, 0, str.ptr, str.slen, tmp, size);
+ tmp[size-1] = 0;
+
+#ifdef UNICODE
+
+ tchar = tmp;
+
+#else
+
+ size = WideCharToMultiByte(CP_ACP, 0, tmp, -1, NULL, 0, NULL, NULL);
+ if (size <= 0)
+ {
+ mir_free(tmp);
+ throw _T("Could not convert string to ACP");
+ }
+
+ tchar = (TCHAR *) mir_alloc(size * sizeof(char));
+ if (tchar == NULL)
+ {
+ mir_free(tmp);
+ throw _T("mir_alloc returned NULL");
+ }
+
+ WideCharToMultiByte(CP_ACP, 0, tmp, -1, tchar, size, NULL, NULL);
+
+ mir_free(tmp);
+
+#endif
+ }
+
+ ~SipToTchar()
+ {
+ if (tchar != NULL)
+ mir_free(tchar);
+ }
+
+ TCHAR *detach()
+ {
+ TCHAR *ret = tchar;
+ tchar = NULL;
+ return ret;
+ }
+
+ TCHAR * get() const
+ {
+ return tchar;
+ }
+
+ operator TCHAR *() const
+ {
+ return tchar;
+ }
+
+ const TCHAR & operator[](int pos) const
+ {
+ return tchar[pos];
+ }
+
+private:
+ TCHAR *tchar;
+};
+
+
+static pj_str_t pj_str(const char *str)
+{
+ pj_str_t ret;
+ pj_cstr(&ret, str);
+ return ret;
+}
+
+
+static char * mir_pjstrdup(const pj_str_t *from)
+{
+ char *ret = (char *) mir_alloc(from->slen + 1);
+ strncpy(ret, from->ptr, from->slen);
+ ret[from->slen] = 0;
+ return ret;
+}
+
+
+static int FirstGtZero(int n1, int n2)
+{
+ if (n1 > 0)
+ return n1;
+ return n2;
+}
+
+
+static TCHAR *CleanupSip(TCHAR *str)
+{
+ if (_tcsnicmp(_T("sip:"), str, 4) == 0)
+ return &str[4];
+ else
+ return str;
+}
+
+
+static const TCHAR *CleanupSip(const TCHAR *str)
+{
+ if (_tcsnicmp(_T("sip:"), str, 4) == 0)
+ return &str[4];
+ else
+ return str;
+}
+
+
+static void CleanupNumber(TCHAR *out, int outSize, const TCHAR *number)
+{
+ int pos = 0;
+ int len = lstrlen(number);
+ for(int i = 0; i < len && pos < outSize - 1; ++i)
+ {
+ TCHAR c = number[i];
+
+ if (i == 0 && c == _T('+'))
+ out[pos++] = c;
+ else if (c >= _T('0') && c <= _T('9'))
+ out[pos++] = c;
+ }
+ out[pos] = 0;
+}
+
+
+static void RemoveLtGt(TCHAR *str)
+{
+ int len = lstrlen(str);
+ if (str[0] == _T('<') && str[len-1] == _T('>'))
+ {
+ str[len-1] = 0;
+ memmove(str, &str[1], len * sizeof(TCHAR));
+ }
+}
+
+
+static bool IsValidDTMF(TCHAR c)
+{
+ if (c >= _T('A') && c <= _T('D'))
+ return true;
+ if (c >= _T('0') && c <= _T('9'))
+ return true;
+ if (c == _T('#') || c == _T('*'))
+ return true;
+
+ return false;
+}
+
+
+static const pj_str_t pj_cstr(const char *s)
+{
+ pj_str_t str;
+ str.ptr = (char*)s;
+ str.slen = s ? (pj_ssize_t)strlen(s) : 0;
+ return str;
+}
+
+
+#endif // __STRUTILS_H__