/* $Id$ */ /* * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) * * 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 #include #include #include #include "util.hpp" using namespace pj; using namespace std; #define THIS_FILE "account.cpp" /////////////////////////////////////////////////////////////////////////////// void RtcpFbCap::fromPj(const pjmedia_rtcp_fb_cap &prm) { this->codecId = pj2Str(prm.codec_id); this->type = prm.type; this->typeName = pj2Str(prm.type_name); this->param = pj2Str(prm.param); } pjmedia_rtcp_fb_cap RtcpFbCap::toPj() const { pjmedia_rtcp_fb_cap cap; pj_bzero(&cap, sizeof(cap)); cap.codec_id = str2Pj(this->codecId); cap.type = this->type; cap.type_name = str2Pj(this->typeName); cap.param = str2Pj(this->param); return cap; } /////////////////////////////////////////////////////////////////////////////// RtcpFbConfig::RtcpFbConfig() { pjmedia_rtcp_fb_setting setting; pjmedia_rtcp_fb_setting_default(&setting); fromPj(setting); } void RtcpFbConfig::fromPj(const pjmedia_rtcp_fb_setting &prm) { this->dontUseAvpf = PJ2BOOL(prm.dont_use_avpf); this->caps.clear(); for (unsigned i = 0; i < prm.cap_count; ++i) { RtcpFbCap cap; cap.fromPj(prm.caps[i]); this->caps.push_back(cap); } } pjmedia_rtcp_fb_setting RtcpFbConfig::toPj() const { pjmedia_rtcp_fb_setting setting; pj_bzero(&setting, sizeof(setting)); setting.dont_use_avpf = this->dontUseAvpf; setting.cap_count = this->caps.size(); for (unsigned i = 0; i < setting.cap_count; ++i) { setting.caps[i] = this->caps[i].toPj(); } return setting; } void RtcpFbConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { ContainerNode this_node = node.readContainer("RtcpFbConfig"); NODE_READ_BOOL (this_node, dontUseAvpf); ContainerNode cap_node = this_node.readArray("caps"); this->caps.clear(); while (cap_node.hasUnread()) { RtcpFbCap cap; NODE_READ_STRING (cap_node, cap.codecId); NODE_READ_NUM_T (cap_node, pjmedia_rtcp_fb_type, cap.type); NODE_READ_STRING (cap_node, cap.typeName); NODE_READ_STRING (cap_node, cap.param); this->caps.push_back(cap); } } void RtcpFbConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) { ContainerNode this_node = node.writeNewContainer("RtcpFbConfig"); NODE_WRITE_BOOL (this_node, dontUseAvpf); ContainerNode cap_node = this_node.writeNewArray("caps"); for (unsigned i=0; icaps.size(); ++i) { NODE_WRITE_STRING (cap_node, this->caps[i].codecId); NODE_WRITE_NUM_T (cap_node, pjmedia_rtcp_fb_type, this->caps[i].type); NODE_WRITE_STRING (cap_node, this->caps[i].typeName); NODE_WRITE_STRING (cap_node, this->caps[i].param); } } /////////////////////////////////////////////////////////////////////////////// void SrtpCrypto::fromPj(const pjmedia_srtp_crypto &prm) { this->key = pj2Str(prm.key); this->name = pj2Str(prm.name); this->flags = prm.flags; } pjmedia_srtp_crypto SrtpCrypto::toPj() const { pjmedia_srtp_crypto crypto; crypto.key = str2Pj(this->key); crypto.name = str2Pj(this->name); crypto.flags = this->flags; return crypto; } /////////////////////////////////////////////////////////////////////////////// SrtpOpt::SrtpOpt() { pjsua_srtp_opt opt; pjsua_srtp_opt_default(&opt); fromPj(opt); } void SrtpOpt::fromPj(const pjsua_srtp_opt &prm) { this->cryptos.clear(); for (unsigned i = 0; i < prm.crypto_count; ++i) { SrtpCrypto crypto; crypto.fromPj(prm.crypto[i]); this->cryptos.push_back(crypto); } this->keyings.clear(); for (unsigned i = 0; i < prm.keying_count; ++i) { this->keyings.push_back(prm.keying[i]); } } pjsua_srtp_opt SrtpOpt::toPj() const { pjsua_srtp_opt opt; pj_bzero(&opt, sizeof(opt)); opt.crypto_count = this->cryptos.size(); for (unsigned i = 0; i < opt.crypto_count; ++i) { opt.crypto[i] = this->cryptos[i].toPj(); } opt.keying_count = this->keyings.size(); for (unsigned i = 0; i < opt.keying_count; ++i) { opt.keying[i] = (pjmedia_srtp_keying_method)this->keyings[i]; } return opt; } void SrtpOpt::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { ContainerNode this_node = node.readContainer("SrtpOpt"); ContainerNode crypto_node = this_node.readArray("cryptos"); this->cryptos.clear(); while (crypto_node.hasUnread()) { SrtpCrypto crypto; NODE_READ_STRING (crypto_node, crypto.key); NODE_READ_STRING (crypto_node, crypto.name); NODE_READ_UNSIGNED (crypto_node, crypto.flags); this->cryptos.push_back(crypto); } ContainerNode keying_node = this_node.readArray("keyings"); this->keyings.clear(); while (keying_node.hasUnread()) { unsigned keying; NODE_READ_UNSIGNED (keying_node, keying); this->keyings.push_back(keying); } } void SrtpOpt::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) { ContainerNode this_node = node.writeNewContainer("SrtpOpt"); ContainerNode crypto_node = this_node.writeNewArray("cryptos"); for (unsigned i=0; icryptos.size(); ++i) { NODE_WRITE_STRING (crypto_node, this->cryptos[i].key); NODE_WRITE_STRING (crypto_node, this->cryptos[i].name); NODE_WRITE_UNSIGNED (crypto_node, this->cryptos[i].flags); } ContainerNode keying_node = this_node.writeNewArray("keyings"); for (unsigned i=0; ikeyings.size(); ++i) { NODE_WRITE_UNSIGNED (keying_node, this->keyings[i]); } } /////////////////////////////////////////////////////////////////////////////// void AccountRegConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { ContainerNode this_node = node.readContainer("AccountRegConfig"); NODE_READ_STRING (this_node, registrarUri); NODE_READ_BOOL (this_node, registerOnAdd); NODE_READ_UNSIGNED (this_node, timeoutSec); NODE_READ_UNSIGNED (this_node, retryIntervalSec); NODE_READ_UNSIGNED (this_node, firstRetryIntervalSec); NODE_READ_UNSIGNED (this_node, randomRetryIntervalSec); NODE_READ_UNSIGNED (this_node, delayBeforeRefreshSec); NODE_READ_BOOL (this_node, dropCallsOnFail); NODE_READ_UNSIGNED (this_node, unregWaitMsec); NODE_READ_UNSIGNED (this_node, proxyUse); NODE_READ_STRING (this_node, contactParams); readSipHeaders(this_node, "headers", headers); } void AccountRegConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) { ContainerNode this_node = node.writeNewContainer("AccountRegConfig"); NODE_WRITE_STRING (this_node, registrarUri); NODE_WRITE_BOOL (this_node, registerOnAdd); NODE_WRITE_UNSIGNED (this_node, timeoutSec); NODE_WRITE_UNSIGNED (this_node, retryIntervalSec); NODE_WRITE_UNSIGNED (this_node, firstRetryIntervalSec); NODE_WRITE_UNSIGNED (this_node, randomRetryIntervalSec); NODE_WRITE_UNSIGNED (this_node, delayBeforeRefreshSec); NODE_WRITE_BOOL (this_node, dropCallsOnFail); NODE_WRITE_UNSIGNED (this_node, unregWaitMsec); NODE_WRITE_UNSIGNED (this_node, proxyUse); NODE_WRITE_STRING (this_node, contactParams); writeSipHeaders(this_node, "headers", headers); } /////////////////////////////////////////////////////////////////////////////// void AccountSipConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { ContainerNode this_node = node.readContainer("AccountSipConfig"); NODE_READ_STRINGV (this_node, proxies); NODE_READ_STRING (this_node, contactForced); NODE_READ_STRING (this_node, contactParams); NODE_READ_STRING (this_node, contactUriParams); NODE_READ_BOOL (this_node, authInitialEmpty); NODE_READ_STRING (this_node, authInitialAlgorithm); NODE_READ_INT (this_node, transportId); ContainerNode creds_node = this_node.readArray("authCreds"); authCreds.resize(0); while (creds_node.hasUnread()) { AuthCredInfo cred; cred.readObject(creds_node); authCreds.push_back(cred); } } void AccountSipConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) { ContainerNode this_node = node.writeNewContainer("AccountSipConfig"); NODE_WRITE_STRINGV (this_node, proxies); NODE_WRITE_STRING (this_node, contactForced); NODE_WRITE_STRING (this_node, contactParams); NODE_WRITE_STRING (this_node, contactUriParams); NODE_WRITE_BOOL (this_node, authInitialEmpty); NODE_WRITE_STRING (this_node, authInitialAlgorithm); NODE_WRITE_INT (this_node, transportId); ContainerNode creds_node = this_node.writeNewArray("authCreds"); for (unsigned i=0; i PJ_ARRAY_SIZE(ret.cred_info)) PJSUA2_RAISE_ERROR(PJ_ETOOMANY); for (i=0; irealm = str2Pj(src.realm); dst->scheme = str2Pj(src.scheme); dst->username = str2Pj(src.username); dst->data_type = src.dataType; dst->data = str2Pj(src.data); dst->ext.aka.k = str2Pj(src.akaK); dst->ext.aka.op = str2Pj(src.akaOp); dst->ext.aka.amf= str2Pj(src.akaAmf); ret.cred_count++; } ret.proxy_cnt = 0; if (sipConfig.proxies.size() > PJ_ARRAY_SIZE(ret.proxy)) PJSUA2_RAISE_ERROR(PJ_ETOOMANY); for (i=0; inext; } // AccountSipConfig sipConfig.authCreds.clear(); for (i=0; inext; } presConfig.publishEnabled = PJ2BOOL(prm.publish_enabled); presConfig.publishQueue = PJ2BOOL(prm.publish_opt.queue_request); presConfig.publishShutdownWaitMsec = prm.unpublish_max_wait_time_msec; presConfig.pidfTupleId = pj2Str(prm.pidf_tuple_id); // AccountMwiConfig mwiConfig.enabled = PJ2BOOL(prm.mwi_enabled); mwiConfig.expirationSec = prm.mwi_expires; // AccountNatConfig natConfig.sipStunUse = prm.sip_stun_use; natConfig.mediaStunUse = prm.media_stun_use; natConfig.nat64Opt = prm.nat64_opt; if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) { natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice); natConfig.iceMaxHostCands = prm.ice_cfg.ice_max_host_cands; natConfig.iceAggressiveNomination = PJ2BOOL(prm.ice_cfg.ice_opt.aggressive); natConfig.iceNominatedCheckDelayMsec = prm.ice_cfg.ice_opt.nominated_check_delay; natConfig.iceWaitNominationTimeoutMsec = prm.ice_cfg.ice_opt.controlled_agent_want_nom_timeout; natConfig.iceNoRtcp = PJ2BOOL(prm.ice_cfg.ice_no_rtcp); natConfig.iceAlwaysUpdate = PJ2BOOL(prm.ice_cfg.ice_always_update); } else { pjsua_media_config default_mcfg; if (!mcfg) { pjsua_media_config_default(&default_mcfg); mcfg = &default_mcfg; } natConfig.iceEnabled = PJ2BOOL(mcfg->enable_ice); natConfig.iceMaxHostCands= mcfg->ice_max_host_cands; natConfig.iceAggressiveNomination = PJ2BOOL(mcfg->ice_opt.aggressive); natConfig.iceNominatedCheckDelayMsec = mcfg->ice_opt.nominated_check_delay; natConfig.iceWaitNominationTimeoutMsec = mcfg->ice_opt.controlled_agent_want_nom_timeout; natConfig.iceNoRtcp = PJ2BOOL(mcfg->ice_no_rtcp); natConfig.iceAlwaysUpdate = PJ2BOOL(mcfg->ice_always_update); } if (prm.turn_cfg_use == PJSUA_TURN_CONFIG_USE_CUSTOM) { natConfig.turnEnabled = PJ2BOOL(prm.turn_cfg.enable_turn); natConfig.turnServer = pj2Str(prm.turn_cfg.turn_server); natConfig.turnConnType = prm.turn_cfg.turn_conn_type; natConfig.turnUserName = pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.username); natConfig.turnPasswordType = prm.turn_cfg.turn_auth_cred.data.static_cred.data_type; natConfig.turnPassword = pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.data); } else { pjsua_media_config default_mcfg; if (!mcfg) { pjsua_media_config_default(&default_mcfg); mcfg = &default_mcfg; } natConfig.turnEnabled = PJ2BOOL(mcfg->enable_turn); natConfig.turnServer = pj2Str(mcfg->turn_server); natConfig.turnConnType = mcfg->turn_conn_type; natConfig.turnUserName = pj2Str(mcfg->turn_auth_cred.data.static_cred.username); natConfig.turnPasswordType = mcfg->turn_auth_cred.data.static_cred.data_type; natConfig.turnPassword = pj2Str(mcfg->turn_auth_cred.data.static_cred.data); } natConfig.contactRewriteUse = prm.allow_contact_rewrite; natConfig.contactRewriteMethod = prm.contact_rewrite_method; natConfig.contactUseSrcPort = prm.contact_use_src_port; natConfig.viaRewriteUse = prm.allow_via_rewrite; natConfig.sdpNatRewriteUse = prm.allow_sdp_nat_rewrite; natConfig.sipOutboundUse = prm.use_rfc5626; natConfig.sipOutboundInstanceId = pj2Str(prm.rfc5626_instance_id); natConfig.sipOutboundRegId = pj2Str(prm.rfc5626_reg_id); natConfig.udpKaIntervalSec = prm.ka_interval; natConfig.udpKaData = pj2Str(prm.ka_data); // AccountMediaConfig mediaConfig.transportConfig.fromPj(prm.rtp_cfg); mediaConfig.lockCodecEnabled= PJ2BOOL(prm.lock_codec); #if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0) mediaConfig.streamKaEnabled = PJ2BOOL(prm.use_stream_ka); #else mediaConfig.streamKaEnabled = false; #endif mediaConfig.srtpUse = prm.use_srtp; mediaConfig.srtpSecureSignaling = prm.srtp_secure_signaling; mediaConfig.srtpOpt.fromPj(prm.srtp_opt); mediaConfig.ipv6Use = prm.ipv6_media_use; mediaConfig.rtcpMuxEnabled = PJ2BOOL(prm.enable_rtcp_mux); mediaConfig.rtcpFbConfig.fromPj(prm.rtcp_fb_cfg); // AccountVideoConfig videoConfig.autoShowIncoming = PJ2BOOL(prm.vid_in_auto_show); videoConfig.autoTransmitOutgoing = PJ2BOOL(prm.vid_out_auto_transmit); videoConfig.windowFlags = prm.vid_wnd_flags; videoConfig.defaultCaptureDevice = prm.vid_cap_dev; videoConfig.defaultRenderDevice = prm.vid_rend_dev; videoConfig.rateControlMethod = prm.vid_stream_rc_cfg.method; videoConfig.rateControlBandwidth = prm.vid_stream_rc_cfg.bandwidth; videoConfig.startKeyframeCount = prm.vid_stream_sk_cfg.count; videoConfig.startKeyframeInterval = prm.vid_stream_sk_cfg.interval; // AccountIpChangeConfig ipChangeConfig.shutdownTp = PJ2BOOL(prm.ip_change_cfg.shutdown_tp); ipChangeConfig.hangupCalls = PJ2BOOL(prm.ip_change_cfg.hangup_calls); ipChangeConfig.reinviteFlags = prm.ip_change_cfg.reinvite_flags; } void AccountConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) { ContainerNode this_node = node.readContainer("AccountConfig"); NODE_READ_INT ( this_node, priority); NODE_READ_STRING ( this_node, idUri); NODE_READ_OBJ ( this_node, regConfig); NODE_READ_OBJ ( this_node, sipConfig); NODE_READ_OBJ ( this_node, callConfig); NODE_READ_OBJ ( this_node, presConfig); NODE_READ_OBJ ( this_node, mwiConfig); NODE_READ_OBJ ( this_node, natConfig); NODE_READ_OBJ ( this_node, mediaConfig); NODE_READ_OBJ ( this_node, videoConfig); } void AccountConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) { ContainerNode this_node = node.writeNewContainer("AccountConfig"); NODE_WRITE_INT ( this_node, priority); NODE_WRITE_STRING ( this_node, idUri); NODE_WRITE_OBJ ( this_node, regConfig); NODE_WRITE_OBJ ( this_node, sipConfig); NODE_WRITE_OBJ ( this_node, callConfig); NODE_WRITE_OBJ ( this_node, presConfig); NODE_WRITE_OBJ ( this_node, mwiConfig); NODE_WRITE_OBJ ( this_node, natConfig); NODE_WRITE_OBJ ( this_node, mediaConfig); NODE_WRITE_OBJ ( this_node, videoConfig); } /////////////////////////////////////////////////////////////////////////////// void AccountInfo::fromPj(const pjsua_acc_info &pai) { id = pai.id; isDefault = pai.is_default != 0; uri = pj2Str(pai.acc_uri); regIsConfigured = pai.has_registration != 0; regIsActive = pai.has_registration && pai.expires > 0 && pai.expires != PJSIP_EXPIRES_NOT_SPECIFIED && (pai.status / 100 == 2); regExpiresSec = pai.expires; regStatus = pai.status; regStatusText = pj2Str(pai.status_text); regLastErr = pai.reg_last_err; onlineStatus = pai.online_status != 0; onlineStatusText = pj2Str(pai.online_status_text); } /////////////////////////////////////////////////////////////////////////////// Account::Account() : id(PJSUA_INVALID_ID) { } Account::~Account() { /* If this instance is deleted, also delete the corresponding account in * PJSUA library. */ shutdown(); } void Account::create(const AccountConfig &acc_cfg, bool make_default) PJSUA2_THROW(Error) { pjsua_acc_config pj_acc_cfg; acc_cfg.toPj(pj_acc_cfg); pj_acc_cfg.user_data = (void*)this; PJSUA2_CHECK_EXPR( pjsua_acc_add(&pj_acc_cfg, make_default, &id) ); } void Account::shutdown() { if (isValid() && pjsua_get_state() < PJSUA_STATE_CLOSING) { #if !DEPRECATED_FOR_TICKET_2232 // Cleanup buddies in the buddy list while(buddyList.size() > 0) { Buddy *b = buddyList[0]; delete b; /* this will remove itself from the list */ } #endif // This caused error message of "Error: cannot find Account.." // when Endpoint::on_reg_started() is called for unregistration. //pjsua_acc_set_user_data(id, NULL); pjsua_acc_del(id); } } void Account::modify(const AccountConfig &acc_cfg) PJSUA2_THROW(Error) { pjsua_acc_config pj_acc_cfg; acc_cfg.toPj(pj_acc_cfg); pj_acc_cfg.user_data = (void*)this; PJSUA2_CHECK_EXPR( pjsua_acc_modify(id, &pj_acc_cfg) ); } bool Account::isValid() const { return pjsua_acc_is_valid(id) != 0; } void Account::setDefault() PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_acc_set_default(id) ); } bool Account::isDefault() const { return pjsua_acc_get_default() == id; } int Account::getId() const { return id; } Account *Account::lookup(int acc_id) { return (Account*)pjsua_acc_get_user_data(acc_id); } AccountInfo Account::getInfo() const PJSUA2_THROW(Error) { pjsua_acc_info pj_ai; AccountInfo ai; PJSUA2_CHECK_EXPR( pjsua_acc_get_info(id, &pj_ai) ); ai.fromPj(pj_ai); return ai; } void Account::setRegistration(bool renew) PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_acc_set_registration(id, renew) ); } void Account::setOnlineStatus(const PresenceStatus &pres_st) PJSUA2_THROW(Error) { pjrpid_element pj_rpid; pj_bzero(&pj_rpid, sizeof(pj_rpid)); pj_rpid.type = PJRPID_ELEMENT_TYPE_PERSON; pj_rpid.activity = pres_st.activity; pj_rpid.id = str2Pj(pres_st.rpidId); pj_rpid.note = str2Pj(pres_st.note); PJSUA2_CHECK_EXPR( pjsua_acc_set_online_status2( id, pres_st.status == PJSUA_BUDDY_STATUS_ONLINE, &pj_rpid) ); } void Account::setTransport(TransportId tp_id) PJSUA2_THROW(Error) { PJSUA2_CHECK_EXPR( pjsua_acc_set_transport(id, tp_id) ); } void Account::presNotify(const PresNotifyParam &prm) PJSUA2_THROW(Error) { pj_str_t pj_state_str = str2Pj(prm.stateStr); pj_str_t pj_reason = str2Pj(prm.reason); pjsua_msg_data msg_data; prm.txOption.toPj(msg_data); PJSUA2_CHECK_EXPR( pjsua_pres_notify(id, (pjsua_srv_pres*)prm.srvPres, prm.state, &pj_state_str, &pj_reason, prm.withBody, &msg_data) ); } #if !DEPRECATED_FOR_TICKET_2232 const BuddyVector& Account::enumBuddies() const PJSUA2_THROW(Error) { return buddyList; } #endif BuddyVector2 Account::enumBuddies2() const PJSUA2_THROW(Error) { BuddyVector2 bv2; pjsua_buddy_id ids[PJSUA_MAX_BUDDIES]; unsigned i, count = PJSUA_MAX_BUDDIES; PJSUA2_CHECK_EXPR( pjsua_enum_buddies(ids, &count) ); for (i = 0; i < count; ++i) { bv2.push_back(Buddy(ids[i])); } return bv2; } #if !DEPRECATED_FOR_TICKET_2232 Buddy* Account::findBuddy(string uri, FindBuddyMatch *buddy_match) const PJSUA2_THROW(Error) { if (!buddy_match) { static FindBuddyMatch def_bm; buddy_match = &def_bm; } for (unsigned i = 0; i < buddyList.size(); i++) { if (buddy_match->match(uri, *buddyList[i])) return buddyList[i]; } PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); } #endif Buddy Account::findBuddy2(string uri) const PJSUA2_THROW(Error) { pj_str_t pj_uri; pjsua_buddy_id bud_id; pj_strset2(&pj_uri, (char*)uri.c_str()); bud_id = pjsua_buddy_find(&pj_uri); if (id == PJSUA_INVALID_ID) { PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); } Buddy buddy(bud_id); return buddy; } void Account::addBuddy(Buddy *buddy) { #if !DEPRECATED_FOR_TICKET_2232 pj_assert(buddy); buddyList.push_back(buddy); #else PJ_UNUSED_ARG(buddy); #endif } void Account::removeBuddy(Buddy *buddy) { #if !DEPRECATED_FOR_TICKET_2232 pj_assert(buddy); BuddyVector::iterator it; for (it = buddyList.begin(); it != buddyList.end(); it++) { if (*it == buddy) { buddyList.erase(it); return; } } pj_assert(!"Bug! Buddy to be removed is not in the buddy list!"); #else PJ_UNUSED_ARG(buddy); #endif }