mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-07 22:02:12 +08:00
Remove exosip stuff, correct warnings in sipcall
This commit is contained in:
30
configure.ac
30
configure.ac
@ -126,9 +126,7 @@ AC_CHECK_FUNCS([bzero floor gethostbyname gethrtime gettimeofday \
|
|||||||
inet_ntoa memset mkdir pathconf pow regcomp select setlocale socket \
|
inet_ntoa memset mkdir pathconf pow regcomp select setlocale socket \
|
||||||
strchr strdup strerror strrchr strstr strtol utime])
|
strchr strdup strerror strrchr strstr strtol utime])
|
||||||
|
|
||||||
dnl Check for exosip2
|
SFLPHONE_LIBS="$SFLPHONE_LIBS"
|
||||||
LP_CHECK_EXOSIP2
|
|
||||||
SFLPHONE_LIBS="$SFLPHONE_LIBS $EXOSIP_LIBS"
|
|
||||||
|
|
||||||
dnl Check for GNU ccRTP
|
dnl Check for GNU ccRTP
|
||||||
PKG_PROG_PKG_CONFIG
|
PKG_PROG_PKG_CONFIG
|
||||||
@ -139,12 +137,6 @@ if test "x${have_libpj}" = "xfalse" ; then
|
|||||||
AC_MSG_ERROR([PJSIP not found. http://www.pjsip.org/download.htm])
|
AC_MSG_ERROR([PJSIP not found. http://www.pjsip.org/download.htm])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LIBOSIP2_OSIPLISTNOPOINTER_MIN_VERSION=3.0.0
|
|
||||||
PKG_CHECK_MODULES([libosip2], [libosip2 >= ${LIBOSIP2_OSIPLISTNOPOINTER_MIN_VERSION}],
|
|
||||||
[LIBOSIP2_CFLAGS=""], [LIBOSIP2_CFLAGS="-DLIBOSIP2_WITHPOINTER"])
|
|
||||||
SIP_CFLAGS="$SIP_CFLAGS $LIBOSIP2_CFLAGS";
|
|
||||||
AC_SUBST(SIP_CFLAGS)
|
|
||||||
|
|
||||||
LIBASOUND2_MIN_VERSION=1.0
|
LIBASOUND2_MIN_VERSION=1.0
|
||||||
PKG_CHECK_MODULES(alsa, alsa >= ${LIBASOUND2_MIN_VERSION})
|
PKG_CHECK_MODULES(alsa, alsa >= ${LIBASOUND2_MIN_VERSION})
|
||||||
SFLPHONE_CFLAGS="$SFLPHONE_CFLAGS $libasound2_CFLAGS"
|
SFLPHONE_CFLAGS="$SFLPHONE_CFLAGS $libasound2_CFLAGS"
|
||||||
@ -349,26 +341,6 @@ DBUS_SERVICES_DIR="$datadir/dbus-1/services"
|
|||||||
AC_SUBST(DBUS_SERVICES_DIR)
|
AC_SUBST(DBUS_SERVICES_DIR)
|
||||||
AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is])
|
AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is])
|
||||||
|
|
||||||
|
|
||||||
dnl QT interface
|
|
||||||
AC_MSG_CHECKING([whether to enable sflphoneqt])
|
|
||||||
AC_ARG_ENABLE(sflphoneqt,
|
|
||||||
AS_HELP_STRING(
|
|
||||||
[--enable-sflphoneqt],
|
|
||||||
[enable sflphone-qt compilation @<:@default=no@:>@]
|
|
||||||
),
|
|
||||||
[with_sflphoneqt=$enableval],
|
|
||||||
[with_sflphoneqt=no]
|
|
||||||
)
|
|
||||||
AM_CONDITIONAL(WITH_QT, test "x$with_sflphoneqt" = "xyes")
|
|
||||||
AM_CONDITIONAL(include_x11, test x$with_sflphoneqt = "xyes")
|
|
||||||
if test "x$with_sflphoneqt" = "xno"; then
|
|
||||||
AC_MSG_RESULT(no)
|
|
||||||
else
|
|
||||||
AC_MSG_RESULT(yes)
|
|
||||||
gw_CHECK_QT
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Go!
|
# Go!
|
||||||
AC_SUBST(SFLPHONE_CFLAGS)
|
AC_SUBST(SFLPHONE_CFLAGS)
|
||||||
AC_SUBST(SFLPHONE_LIBS)
|
AC_SUBST(SFLPHONE_LIBS)
|
||||||
|
@ -54,7 +54,7 @@ sflphoned_SOURCES = \
|
|||||||
sflphoned_CXXFLAGS = -DPREFIX=\"$(prefix)\" -DPROGSHAREDIR=\"${datadir}/sflphone\" $(ZEROCONFFLAGS) $(IAX_FLAGS) $(SFLPHONE_CFLAGS) $(SIP_CFLAGS)
|
sflphoned_CXXFLAGS = -DPREFIX=\"$(prefix)\" -DPROGSHAREDIR=\"${datadir}/sflphone\" $(ZEROCONFFLAGS) $(IAX_FLAGS) $(SFLPHONE_CFLAGS) $(SIP_CFLAGS)
|
||||||
|
|
||||||
#sflphoned_LDFLAGS = -static
|
#sflphoned_LDFLAGS = -static
|
||||||
sflphoned_LDADD = ./libsflphone.la $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) $(EXOSIP_LIBS) $(ALSAFLAG) $(PULSEAUDIO_LIBS) $(PJSIP_LIBS)
|
sflphoned_LDADD = ./libsflphone.la $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) $(ALSAFLAG) $(PULSEAUDIO_LIBS) $(PJSIP_LIBS)
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $(libccgnu2_CFLAGS) $(IAX_CFLAGS) $(USER_INCLUDES) $(libdbuscpp_CFLAGS) \
|
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $(libccgnu2_CFLAGS) $(IAX_CFLAGS) $(USER_INCLUDES) $(libdbuscpp_CFLAGS) \
|
||||||
-DCODECS_DIR=\""$(sflcodecdir)"\"
|
-DCODECS_DIR=\""$(sflcodecdir)"\"
|
||||||
-DENABLE_TRACE
|
-DENABLE_TRACE
|
||||||
|
1
src/managerimpl.cpp
Executable file → Normal file
1
src/managerimpl.cpp
Executable file → Normal file
@ -698,7 +698,6 @@ ManagerImpl::callFailure(const CallID& id)
|
|||||||
void
|
void
|
||||||
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_msg)
|
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_msg)
|
||||||
{
|
{
|
||||||
_debug("cccccccccccccccccc\n");
|
|
||||||
if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ;
|
if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
src/managerimpl.h
Executable file → Normal file
0
src/managerimpl.h
Executable file → Normal file
359
src/sipcall.cpp
Executable file → Normal file
359
src/sipcall.cpp
Executable file → Normal file
@ -60,7 +60,7 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool)
|
|||||||
|
|
||||||
_localSDP->origin.version = 0;
|
_localSDP->origin.version = 0;
|
||||||
sdpAddOrigin();
|
sdpAddOrigin();
|
||||||
_localSDP->name = pj_str("sflphone");
|
_localSDP->name = pj_str((char*)"sflphone");
|
||||||
sdpAddConnectionInfo();
|
sdpAddConnectionInfo();
|
||||||
_localSDP->time.start = _localSDP->time.stop = 0;
|
_localSDP->time.start = _localSDP->time.stop = 0;
|
||||||
sdpAddMediaDescription(pool);
|
sdpAddMediaDescription(pool);
|
||||||
@ -101,145 +101,6 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
SIPCall::SIPCallReinvite(eXosip_event_t *event)
|
|
||||||
{
|
|
||||||
if (event->cid < 1 && event->did < 1) {
|
|
||||||
_debug("SIP Failure: Invalid cid and did\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->request == NULL) {
|
|
||||||
_debug("SIP Failure: No request into the event\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCid(event->cid);
|
|
||||||
setDid(event->did);
|
|
||||||
setTid(event->tid);
|
|
||||||
|
|
||||||
setPeerInfoFromRequest(event);
|
|
||||||
|
|
||||||
sdp_message_t* remote_sdp = getRemoteSDPFromRequest(event);
|
|
||||||
if (remote_sdp == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdp_media_t* remote_med = getRemoteMedia(event->tid, remote_sdp);
|
|
||||||
if (remote_med == 0) {
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (!setRemoteAudioFromSDP(remote_med, remote_sdp)) {
|
|
||||||
_debug("SIP Failure: unable to set IP address and port from SDP\n");
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!setAudioCodecFromSDP(remote_med, event->tid)) {
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
return false;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
osip_message_t *answer = 0;
|
|
||||||
eXosip_lock();
|
|
||||||
_debug("< Building Answer 200\n");
|
|
||||||
if (0 == eXosip_call_build_answer (event->tid, 200, &answer)) {
|
|
||||||
if ( 0 != sdp_complete_message(remote_sdp, answer)) {
|
|
||||||
osip_message_free(answer);
|
|
||||||
// Send 415 Unsupported media type
|
|
||||||
eXosip_call_send_answer (event->tid, 415, NULL);
|
|
||||||
_debug("< Sending Answer 415\n");
|
|
||||||
} else {
|
|
||||||
|
|
||||||
sdp_message_t *local_sdp = eXosip_get_sdp_info(answer);
|
|
||||||
sdp_media_t *local_med = NULL;
|
|
||||||
if (local_sdp != NULL) {
|
|
||||||
local_med = eXosip_get_audio_media(local_sdp);
|
|
||||||
}
|
|
||||||
if (local_sdp != NULL && local_med != NULL) {
|
|
||||||
/* search if stream is sendonly or recvonly */
|
|
||||||
int _remote_sendrecv = sdp_analyse_attribute (remote_sdp, remote_med);
|
|
||||||
int _local_sendrecv = sdp_analyse_attribute (local_sdp, local_med);
|
|
||||||
_debug(" Remote SendRecv: %d\n", _remote_sendrecv);
|
|
||||||
_debug(" Local SendRecv: %d\n", _local_sendrecv);
|
|
||||||
if (_local_sendrecv == _SENDRECV) {
|
|
||||||
if (_remote_sendrecv == _SENDONLY) { _local_sendrecv = _RECVONLY; }
|
|
||||||
else if (_remote_sendrecv == _RECVONLY) { _local_sendrecv = _SENDONLY; }
|
|
||||||
}
|
|
||||||
_debug(" Final Local SendRecv: %d\n", _local_sendrecv);
|
|
||||||
sdp_message_free (local_sdp);
|
|
||||||
}
|
|
||||||
_debug("< Sending answer 200\n");
|
|
||||||
if (0 != eXosip_call_send_answer (event->tid, 200, answer)) {
|
|
||||||
_debug("SipCall::newIncomingCall: cannot send 200 OK?\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eXosip_unlock ();
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SIPCall::SIPCallAnswered(eXosip_event_t *event)
|
|
||||||
{
|
|
||||||
if (event->cid < 1 && event->did < 1) {
|
|
||||||
_debug("SIP Failure: Invalid cid and did\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->request == NULL) {
|
|
||||||
_debug("SIP Failure: No request into the event\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCid(event->cid);
|
|
||||||
setDid(event->did);
|
|
||||||
|
|
||||||
//setPeerInfoFromResponse()
|
|
||||||
|
|
||||||
eXosip_lock ();
|
|
||||||
{
|
|
||||||
osip_message_t *ack = NULL;
|
|
||||||
int i;
|
|
||||||
i = eXosip_call_build_ack (event->did, &ack);
|
|
||||||
if (i != 0) {
|
|
||||||
_debug("SipCall::answeredCall: Cannot build ACK for call!\n");
|
|
||||||
} else {
|
|
||||||
sdp_message_t *local_sdp = NULL;
|
|
||||||
sdp_message_t *remote_sdp = NULL;
|
|
||||||
|
|
||||||
if (event->request != NULL && event->response != NULL) {
|
|
||||||
local_sdp = eXosip_get_sdp_info (event->request);
|
|
||||||
remote_sdp = eXosip_get_sdp_info (event->response);
|
|
||||||
}
|
|
||||||
if (local_sdp == NULL && remote_sdp != NULL) {
|
|
||||||
/* sdp in ACK */
|
|
||||||
i = sdp_complete_message (remote_sdp, ack);
|
|
||||||
if (i != 0) {
|
|
||||||
_debug("SipCall::answeredCall: Cannot complete ACK with sdp body?!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sdp_message_free (local_sdp);
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
|
|
||||||
_debug("< Send ACK\n");
|
|
||||||
eXosip_call_send_ack (event->did, ack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eXosip_unlock ();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
SIPCall::SIPCallAnsweredWithoutHold(eXosip_event_t* event)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SIPCall::SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata)
|
SIPCall::SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata)
|
||||||
{
|
{
|
||||||
@ -270,193 +131,6 @@ SIPCall::SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
SIPCall::sdp_complete_message(sdp_message_t * remote_sdp, osip_message_t * msg)
|
|
||||||
{
|
|
||||||
// Format port to a char*
|
|
||||||
if (remote_sdp == NULL) {
|
|
||||||
_debug("SipCall::sdp_complete_message: No remote SDP body found for call\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (msg == NULL) {
|
|
||||||
_debug("SipCall::sdp_complete_message: No message to complete\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream media;
|
|
||||||
|
|
||||||
// for each medias
|
|
||||||
int iMedia = 0;
|
|
||||||
char *tmp = NULL;
|
|
||||||
#ifdef LIBOSIP2_WITHPOINTER
|
|
||||||
const osip_list_t* remote_sdp_m_medias = remote_sdp->m_medias; // old abi
|
|
||||||
#else
|
|
||||||
const osip_list_t* remote_sdp_m_medias = &(remote_sdp->m_medias);
|
|
||||||
#endif
|
|
||||||
osip_list_t* remote_med_m_payloads = 0;
|
|
||||||
|
|
||||||
while (!osip_list_eol(remote_sdp_m_medias, iMedia)) {
|
|
||||||
sdp_media_t *remote_med = (sdp_media_t *)osip_list_get(remote_sdp_m_medias, iMedia);
|
|
||||||
if (remote_med == 0) { continue; }
|
|
||||||
|
|
||||||
if (0 != osip_strcasecmp (remote_med->m_media, "audio")) {
|
|
||||||
// if this is not an "audio" media, we set it to 0
|
|
||||||
media << "m=" << remote_med->m_media << " 0 " << remote_med->m_proto << " \r\n";
|
|
||||||
} else {
|
|
||||||
std::ostringstream listCodec;
|
|
||||||
std::ostringstream listRtpMap;
|
|
||||||
|
|
||||||
// search for compatible codec: foreach payload
|
|
||||||
int iPayload = 0;
|
|
||||||
#ifdef LIBOSIP2_WITHPOINTER
|
|
||||||
remote_med_m_payloads = remote_med->m_payloads; // old abi
|
|
||||||
#else
|
|
||||||
remote_med_m_payloads = &(remote_med->m_payloads);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//while (!osip_list_eol(remote_med_m_payloads, iPayload) && iPayload < 2) {
|
|
||||||
while (!osip_list_eol(remote_med_m_payloads, iPayload)) {
|
|
||||||
tmp = (char *)osip_list_get(remote_med_m_payloads, iPayload);
|
|
||||||
if (tmp!=NULL) {
|
|
||||||
int payload = atoi(tmp);
|
|
||||||
_debug("remote payload = %s\n", tmp);
|
|
||||||
AudioCodecType audiocodec = (AudioCodecType)payload;
|
|
||||||
if (audiocodec != (AudioCodecType)-1 && _codecMap.isActive(audiocodec)) {
|
|
||||||
listCodec << payload << " ";
|
|
||||||
//listRtpMap << "a=rtpmap:" << payload << " " << audiocodec->getCodecName() << "/" << audiocodec->getClockRate();
|
|
||||||
listRtpMap << "a=rtpmap:" << payload << " " << _codecMap.getCodecName(audiocodec) << "/" << _codecMap.getSampleRate(audiocodec);
|
|
||||||
if (_codecMap.getChannel(audiocodec) != 1) {
|
|
||||||
listRtpMap << "/" << _codecMap.getChannel(audiocodec);
|
|
||||||
}
|
|
||||||
listRtpMap << "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iPayload++;
|
|
||||||
}
|
|
||||||
if (listCodec.str().empty()) {
|
|
||||||
media << "m=" << remote_med->m_media << " 0 " << remote_med->m_proto << " \r\n";
|
|
||||||
} else {
|
|
||||||
// we add the media line + a=rtpmap list
|
|
||||||
media << "m=" << remote_med->m_media << " " << getLocalExternAudioPort() << " RTP/AVP " << listCodec.str() << "\r\n";
|
|
||||||
media << listRtpMap.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iMedia++;
|
|
||||||
}
|
|
||||||
char buf[4096];
|
|
||||||
snprintf (buf, 4096,
|
|
||||||
"v=0\r\n"
|
|
||||||
"o=user 0 0 IN IP4 %s\r\n"
|
|
||||||
"s=session\r\n"
|
|
||||||
"c=IN IP4 %s\r\n"
|
|
||||||
"t=0 0\r\n"
|
|
||||||
"%s\n", getLocalIp().c_str(), getLocalIp().c_str(), media.str().c_str());
|
|
||||||
|
|
||||||
osip_message_set_body (msg, buf, strlen (buf));
|
|
||||||
osip_message_set_content_type (msg, "application/sdp");
|
|
||||||
_debug(" sdp: %s", buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
SIPCall::sdp_analyse_attribute (sdp_message_t * sdp, sdp_media_t * med)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
int pos_media;
|
|
||||||
|
|
||||||
/* test media attributes */
|
|
||||||
pos = 0;
|
|
||||||
#ifdef LIBOSIP2_WITHPOINTER
|
|
||||||
const osip_list_t* med_a_attributes = med->a_attributes; // old abi
|
|
||||||
#else
|
|
||||||
const osip_list_t* med_a_attributes = &(med->a_attributes);
|
|
||||||
#endif
|
|
||||||
while (!osip_list_eol (med_a_attributes, pos)) {
|
|
||||||
sdp_attribute_t *at;
|
|
||||||
|
|
||||||
at = (sdp_attribute_t *) osip_list_get (med_a_attributes, pos);
|
|
||||||
if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "sendonly")) {
|
|
||||||
return _SENDONLY;
|
|
||||||
} else if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "recvonly")) {
|
|
||||||
return _RECVONLY;
|
|
||||||
} else if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "sendrecv")) {
|
|
||||||
return _SENDRECV;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test global attributes */
|
|
||||||
pos_media = -1;
|
|
||||||
pos = 0;
|
|
||||||
#ifdef LIBOSIP2_WITHPOINTER
|
|
||||||
const osip_list_t* sdp_a_attributes = sdp->a_attributes; // old abi
|
|
||||||
#else
|
|
||||||
const osip_list_t* sdp_a_attributes = &(sdp->a_attributes);
|
|
||||||
#endif
|
|
||||||
while (!osip_list_eol (sdp_a_attributes, pos)) {
|
|
||||||
sdp_attribute_t *at;
|
|
||||||
|
|
||||||
at = (sdp_attribute_t *) osip_list_get (sdp_a_attributes, pos);
|
|
||||||
if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "sendonly")) {
|
|
||||||
return _SENDONLY;
|
|
||||||
} else if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "recvonly")) {
|
|
||||||
return _RECVONLY;
|
|
||||||
} else if (at->a_att_field != NULL &&
|
|
||||||
0 == strcmp (at->a_att_field, "sendrecv")) {
|
|
||||||
return _SENDRECV;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _SENDRECV;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SIPCall::setPeerInfoFromRequest(eXosip_event_t *event)
|
|
||||||
{
|
|
||||||
// event->request should not be NULL!
|
|
||||||
char remote_uri[256] = "";
|
|
||||||
std::string name("");
|
|
||||||
std::string number("");
|
|
||||||
|
|
||||||
char *tmp = NULL;
|
|
||||||
osip_from_to_str(event->request->from, &tmp);
|
|
||||||
if (tmp != NULL) {
|
|
||||||
snprintf (remote_uri, 255, "%s", tmp);
|
|
||||||
remote_uri[255] = '\0';
|
|
||||||
osip_free (tmp);
|
|
||||||
|
|
||||||
// Get the name/number
|
|
||||||
osip_from_t *from;
|
|
||||||
osip_from_init(&from);
|
|
||||||
osip_from_parse(from, remote_uri);
|
|
||||||
char *tmpname = osip_from_get_displayname(from);
|
|
||||||
if ( tmpname != NULL ) {
|
|
||||||
name = tmpname;
|
|
||||||
}
|
|
||||||
osip_uri_t* url = osip_from_get_url(from);
|
|
||||||
if ( url != NULL && url->username != NULL) {
|
|
||||||
number = url->username;
|
|
||||||
}
|
|
||||||
osip_from_free(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
_debug(" Name: %s\n", name.c_str());
|
|
||||||
_debug(" Number: %s\n", number.c_str());
|
|
||||||
_debug(" Remote URI: %s\n", remote_uri);
|
|
||||||
|
|
||||||
setPeerName(name);
|
|
||||||
setPeerNumber(number);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pjmedia_sdp_session*
|
pjmedia_sdp_session*
|
||||||
SIPCall::getRemoteSDPFromRequest(pjsip_rx_data *rdata)
|
SIPCall::getRemoteSDPFromRequest(pjsip_rx_data *rdata)
|
||||||
{
|
{
|
||||||
@ -472,27 +146,6 @@ SIPCall::getRemoteSDPFromRequest(pjsip_rx_data *rdata)
|
|||||||
return sdp;
|
return sdp;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdp_media_t*
|
|
||||||
SIPCall::getRemoteMedia(int tid, sdp_message_t* remote_sdp)
|
|
||||||
{
|
|
||||||
// Remote Media Port
|
|
||||||
eXosip_lock();
|
|
||||||
sdp_media_t *remote_med = eXosip_get_audio_media(remote_sdp);
|
|
||||||
eXosip_unlock();
|
|
||||||
|
|
||||||
if (remote_med == NULL || remote_med->m_port == NULL) {
|
|
||||||
// no audio media proposed
|
|
||||||
_debug("SIP Failure: unsupported media\n");
|
|
||||||
_debug("< Sending 415 Unsupported media type\n");
|
|
||||||
eXosip_lock();
|
|
||||||
eXosip_call_send_answer (tid, 415, NULL);
|
|
||||||
eXosip_unlock();
|
|
||||||
sdp_message_free (remote_sdp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return remote_med;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SIPCall::setRemoteAudioFromSDP(pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media *remote_med)
|
SIPCall::setRemoteAudioFromSDP(pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media *remote_med)
|
||||||
{
|
{
|
||||||
@ -538,9 +191,9 @@ void SIPCall::sdpAddOrigin( void )
|
|||||||
// Use Network Time Protocol format timestamp to ensure uniqueness.
|
// Use Network Time Protocol format timestamp to ensure uniqueness.
|
||||||
_localSDP->origin.id = tv.sec + 2208988800UL;
|
_localSDP->origin.id = tv.sec + 2208988800UL;
|
||||||
// The type of network ( IN for INternet )
|
// The type of network ( IN for INternet )
|
||||||
_localSDP->origin.net_type = pj_str("IN"); //STR_IN;
|
_localSDP->origin.net_type = pj_str((char*)"IN"); //STR_IN;
|
||||||
// The type of address
|
// The type of address
|
||||||
_localSDP->origin.addr_type = pj_str("IP4"); //STR_IP4;
|
_localSDP->origin.addr_type = pj_str((char*)"IP4"); //STR_IP4;
|
||||||
// The address of the machine from which the session was created
|
// The address of the machine from which the session was created
|
||||||
_localSDP->origin.addr = pj_str( (char*)_ipAddr.c_str() );
|
_localSDP->origin.addr = pj_str( (char*)_ipAddr.c_str() );
|
||||||
}
|
}
|
||||||
@ -562,10 +215,10 @@ void SIPCall::sdpAddMediaDescription(pj_pool_t* pool)
|
|||||||
//nbMedia = getSDPMediaList().size();
|
//nbMedia = getSDPMediaList().size();
|
||||||
_localSDP->media_count = 1;
|
_localSDP->media_count = 1;
|
||||||
|
|
||||||
med->desc.media = pj_str("audio");
|
med->desc.media = pj_str((char*)"audio");
|
||||||
med->desc.port_count = 1;
|
med->desc.port_count = 1;
|
||||||
med->desc.port = getLocalExternAudioPort();
|
med->desc.port = getLocalExternAudioPort();
|
||||||
med->desc.transport = pj_str("RTP/AVP");
|
med->desc.transport = pj_str((char*)"RTP/AVP");
|
||||||
|
|
||||||
CodecsMap::iterator itr;
|
CodecsMap::iterator itr;
|
||||||
itr = _codecMap.getCodecsMap().begin();
|
itr = _codecMap.getCodecsMap().begin();
|
||||||
@ -642,7 +295,7 @@ bool SIPCall::createInitialOffer(pj_pool_t *pool)
|
|||||||
|
|
||||||
_localSDP->origin.version = 0;
|
_localSDP->origin.version = 0;
|
||||||
sdpAddOrigin();
|
sdpAddOrigin();
|
||||||
_localSDP->name = pj_str("sflphone");
|
_localSDP->name = pj_str((char*)"sflphone");
|
||||||
sdpAddConnectionInfo();
|
sdpAddConnectionInfo();
|
||||||
_localSDP->time.start = _localSDP->time.stop = 0;
|
_localSDP->time.start = _localSDP->time.stop = 0;
|
||||||
sdpAddMediaDescription(pool);
|
sdpAddMediaDescription(pool);
|
||||||
|
58
src/sipcall.h
Executable file → Normal file
58
src/sipcall.h
Executable file → Normal file
@ -25,8 +25,6 @@
|
|||||||
#include "audio/codecDescriptor.h"
|
#include "audio/codecDescriptor.h"
|
||||||
#include "sipmanager.h"
|
#include "sipmanager.h"
|
||||||
|
|
||||||
#include <eXosip2/eXosip.h>
|
|
||||||
|
|
||||||
class AudioCodec;
|
class AudioCodec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,37 +50,37 @@ class SIPCall : public Call
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Call Identifier
|
* Call Identifier
|
||||||
* @return int SIP call id : protected by eXosip lock
|
* @return int SIP call id
|
||||||
*/
|
*/
|
||||||
int getCid() { return _cid; }
|
int getCid() { return _cid; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call Identifier
|
* Call Identifier
|
||||||
* @param cid SIP call id : protected by eXosip lock
|
* @param cid SIP call id
|
||||||
*/
|
*/
|
||||||
void setCid(int cid) { _cid = cid ; }
|
void setCid(int cid) { _cid = cid ; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domain identifier
|
* Domain identifier
|
||||||
* @return int SIP domain id : protected by eXosip lock
|
* @return int SIP domain id
|
||||||
*/
|
*/
|
||||||
int getDid() { return _did; }
|
int getDid() { return _did; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domain identifier
|
* Domain identifier
|
||||||
* @param did SIP domain id : protected by eXosip lock
|
* @param did SIP domain id
|
||||||
*/
|
*/
|
||||||
void setDid(int did) { _did = did; }
|
void setDid(int did) { _did = did; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction identifier
|
* Transaction identifier
|
||||||
* @return int SIP transaction id : protected by eXosip lock
|
* @return int SIP transaction id
|
||||||
*/
|
*/
|
||||||
int getTid() { return _tid; }
|
int getTid() { return _tid; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction identifier
|
* Transaction identifier
|
||||||
* @param tid SIP transaction id : protected by eXosip lock
|
* @param tid SIP transaction id
|
||||||
*/
|
*/
|
||||||
void setTid(int tid) { _tid = tid; }
|
void setTid(int tid) { _tid = tid; }
|
||||||
|
|
||||||
@ -95,31 +93,8 @@ class SIPCall : public Call
|
|||||||
*/
|
*/
|
||||||
bool SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool);
|
bool SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool);
|
||||||
|
|
||||||
/**
|
|
||||||
* newReinviteCall is called when the IP-Phone user receives a change in the call
|
|
||||||
* it's almost an newIncomingCall but we send a 200 OK
|
|
||||||
* See: 3.7. Session with re-INVITE (IP Address Change)
|
|
||||||
* @param event eXosip Event
|
|
||||||
* @return bool True if ok
|
|
||||||
*/
|
|
||||||
bool SIPCallReinvite(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer answered to a call (on hold or not)
|
|
||||||
* @param event eXosip Event
|
|
||||||
* @return bool True if ok
|
|
||||||
*/
|
|
||||||
bool SIPCallAnswered(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/** No longer being used */
|
|
||||||
bool SIPCallAnsweredWithoutHold(eXosip_event_t *event);
|
|
||||||
|
|
||||||
bool SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata);
|
bool SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata);
|
||||||
|
|
||||||
/** No longer being used */
|
|
||||||
int sdp_complete_message(sdp_message_t * remote_sdp, osip_message_t * msg);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save IP Address
|
* Save IP Address
|
||||||
* @param ip std::string
|
* @param ip std::string
|
||||||
@ -155,30 +130,13 @@ class SIPCall : public Call
|
|||||||
pjsip_inv_session *getInvSession() {return _invSession;}
|
pjsip_inv_session *getInvSession() {return _invSession;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** No longer being used */
|
|
||||||
int sdp_analyse_attribute (sdp_message_t * sdp, sdp_media_t * med);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set peer name and number with event->request->from
|
|
||||||
* @param event eXosip event
|
|
||||||
* @return bool False if the event is invalid
|
|
||||||
*/
|
|
||||||
bool setPeerInfoFromRequest(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a valid remote SDP or return a 400 bad request response if invalid
|
* Get a valid remote SDP or return a 400 bad request response if invalid
|
||||||
* @param event eXosip event
|
* @param
|
||||||
* @return sdp_message_t* A valid remote_sdp or 0
|
* @return
|
||||||
*/
|
*/
|
||||||
pjmedia_sdp_session* getRemoteSDPFromRequest(pjsip_rx_data *rdata);
|
pjmedia_sdp_session* getRemoteSDPFromRequest(pjsip_rx_data *rdata);
|
||||||
|
|
||||||
/** No longer being used */
|
|
||||||
sdp_message_t *getRemoteSDPFromRequest(eXosip_event_t*&){return NULL;}
|
|
||||||
|
|
||||||
/** No longer being used */
|
|
||||||
sdp_media_t* getRemoteMedia(int tid, sdp_message_t* remote_sdp);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a valid remote media
|
* Get a valid remote media
|
||||||
* @param remote_sdp pjmedia_sdp_session*
|
* @param remote_sdp pjmedia_sdp_session*
|
||||||
|
2
src/sipmanager.cpp
Executable file → Normal file
2
src/sipmanager.cpp
Executable file → Normal file
@ -622,7 +622,6 @@ void SIPManager::set_voicemail_info( AccountID account, pjsip_msg_body *body ){
|
|||||||
|
|
||||||
// We get the notification body
|
// We get the notification body
|
||||||
msg_body = (char*)body->data;
|
msg_body = (char*)body->data;
|
||||||
std::cout << " body message " << msg_body.c_str() << std::endl;
|
|
||||||
|
|
||||||
// We need the position of the first character of the string voice_str
|
// We need the position of the first character of the string voice_str
|
||||||
pos_begin = msg_body.find(voice_str);
|
pos_begin = msg_body.find(voice_str);
|
||||||
@ -699,7 +698,6 @@ pj_bool_t SIPManager::mod_on_rx_request(pjsip_rx_data *rdata) {
|
|||||||
method_name = "NOTIFY";
|
method_name = "NOTIFY";
|
||||||
// Retrieve all the message. Should contains only the method name but ...
|
// Retrieve all the message. Should contains only the method name but ...
|
||||||
request = rdata->msg_info.msg->line.req.method.name.ptr;
|
request = rdata->msg_info.msg->line.req.method.name.ptr;
|
||||||
_debug("request = %s\n", request.c_str());
|
|
||||||
// Check if the message is a notification
|
// Check if the message is a notification
|
||||||
if( request.find( method_name ) != -1 ) {
|
if( request.find( method_name ) != -1 ) {
|
||||||
set_voicemail_info( account_id, rdata->msg_info.msg->body );
|
set_voicemail_info( account_id, rdata->msg_info.msg->body );
|
||||||
|
0
src/sipmanager.h
Executable file → Normal file
0
src/sipmanager.h
Executable file → Normal file
1094
src/sipvoiplink.cpp
Executable file → Normal file
1094
src/sipvoiplink.cpp
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
111
src/sipvoiplink.h
Executable file → Normal file
111
src/sipvoiplink.h
Executable file → Normal file
@ -23,12 +23,18 @@
|
|||||||
|
|
||||||
#include "voiplink.h"
|
#include "voiplink.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <eXosip2/eXosip.h>
|
|
||||||
#include <osip2/osip_mt.h>
|
|
||||||
|
|
||||||
struct pjsip_regc;
|
#include <pjsip.h>
|
||||||
struct pj_str_t;
|
#include <pjlib-util.h>
|
||||||
struct pjmedia_sdp_session;
|
#include <pjlib.h>
|
||||||
|
#include <pjnath/stun_config.h>
|
||||||
|
|
||||||
|
//TODO Remove this include if we don't need anything from it
|
||||||
|
#include <pjsip_simple.h>
|
||||||
|
|
||||||
|
#include <pjsip_ua.h>
|
||||||
|
#include <pjmedia/sdp.h>
|
||||||
|
#include <pjmedia/sdp_neg.h>
|
||||||
|
|
||||||
class EventThread;
|
class EventThread;
|
||||||
class SIPCall;
|
class SIPCall;
|
||||||
@ -54,10 +60,8 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
*/
|
*/
|
||||||
~SIPVoIPLink();
|
~SIPVoIPLink();
|
||||||
|
|
||||||
int eXosip_running;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to initiate the eXosip engine/thread and set config
|
* Try to initiate the pjsip engine/thread and set config
|
||||||
* @return bool True if OK
|
* @return bool True if OK
|
||||||
*/
|
*/
|
||||||
bool init(void);
|
bool init(void);
|
||||||
@ -165,10 +169,6 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
|
|
||||||
bool isContactPresenceSupported();
|
bool isContactPresenceSupported();
|
||||||
|
|
||||||
//void subscribePresenceForContact(Contact* contact);
|
|
||||||
|
|
||||||
void publishPresenceStatus(std::string status);
|
|
||||||
|
|
||||||
// TODO Not used yet
|
// TODO Not used yet
|
||||||
void sendMessageToContact(const CallID& id, const std::string& message);
|
void sendMessageToContact(const CallID& id, const std::string& message);
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
void terminateSIPCall();
|
void terminateSIPCall();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the local Ip by eXosip
|
* Get the local Ip
|
||||||
* only if the local ip address is to his default value: 127.0.0.1
|
* only if the local ip address is to his default value: 127.0.0.1
|
||||||
* setLocalIpAdress
|
* setLocalIpAdress
|
||||||
* @return bool false if not found
|
* @return bool false if not found
|
||||||
@ -293,89 +293,33 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
*/
|
*/
|
||||||
bool setCallAudioLocal(SIPCall* call);
|
bool setCallAudioLocal(SIPCall* call);
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new call and send a incoming call notification to the user
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallInvite(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a exisiting call to restart the audio
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallReinvite(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the user that the call is ringing
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallRinging(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the user that the call was answered
|
* Tell the user that the call was answered
|
||||||
* @param event eXosip Event
|
* @param
|
||||||
*/
|
*/
|
||||||
void SIPCallAnswered(eXosip_event_t *event);
|
|
||||||
void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata);
|
void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handling 4XX error
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallRequestFailure(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handling 5XX/6XX error
|
* Handling 5XX/6XX error
|
||||||
* @param event eXosip Event
|
* @param
|
||||||
*/
|
*/
|
||||||
void SIPCallServerFailure(SIPCall *call);
|
void SIPCallServerFailure(SIPCall *call);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle registration failure cases ( SIP_FORBIDDEN , SIP_UNAUTHORIZED )
|
|
||||||
* @param event eXosip event
|
|
||||||
*/
|
|
||||||
void SIPRegistrationFailure( eXosip_event_t *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handling ack (restart audio if reinvite)
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallAck(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handling message inside a call (like dtmf)
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPCallMessageNew(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an INFO with application/dtmf-relay content-type
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
bool handleDtmfRelay(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peer close the connection
|
* Peer close the connection
|
||||||
* @param event eXosip Event
|
* @param
|
||||||
*/
|
*/
|
||||||
void SIPCallClosed(SIPCall *call);
|
void SIPCallClosed(SIPCall *call);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The call pointer was released
|
* The call pointer was released
|
||||||
* If the call was not cleared before, report an error
|
* If the call was not cleared before, report an error
|
||||||
* @param event eXosip Event
|
* @param
|
||||||
*/
|
*/
|
||||||
void SIPCallReleased(SIPCall *call);
|
void SIPCallReleased(SIPCall *call);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive a new Message request
|
* Find a SIPCall with cid
|
||||||
* Option/Notify/Message
|
|
||||||
* @param event eXosip Event
|
|
||||||
*/
|
|
||||||
void SIPMessageNew(eXosip_event_t *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a SIPCall with cid from eXosip Event
|
|
||||||
* Explication there is no DID when the dialog is not establish...
|
* Explication there is no DID when the dialog is not establish...
|
||||||
* @param cid call ID
|
* @param cid call ID
|
||||||
* @return SIPCall* SIPCall pointer or 0
|
* @return SIPCall* SIPCall pointer or 0
|
||||||
@ -383,7 +327,7 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
SIPCall* findSIPCallWithCid(int cid);
|
SIPCall* findSIPCallWithCid(int cid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a SIPCall with cid and did from eXosip Event
|
* Find a SIPCall with cid and did
|
||||||
* @param cid call ID
|
* @param cid call ID
|
||||||
* @param did domain ID
|
* @param did domain ID
|
||||||
* @return SIPCall* SIPCall pointer or 0
|
* @return SIPCall* SIPCall pointer or 0
|
||||||
@ -397,24 +341,9 @@ class SIPVoIPLink : public VoIPLink
|
|||||||
*/
|
*/
|
||||||
SIPCall* getSIPCall(const CallID& id);
|
SIPCall* getSIPCall(const CallID& id);
|
||||||
|
|
||||||
/** To build sdp when call is on-hold */
|
/** Tell if the initialisation was done */
|
||||||
int sdp_hold_call (sdp_message_t * sdp);
|
|
||||||
|
|
||||||
/** To build sdp when call is off-hold */
|
|
||||||
int sdp_off_hold_call (sdp_message_t * sdp);
|
|
||||||
|
|
||||||
/** EventThread get every incoming events */
|
|
||||||
EventThread* _evThread;
|
|
||||||
|
|
||||||
/** Tell if eXosip was stared (eXosip_init) */
|
|
||||||
bool _initDone;
|
bool _initDone;
|
||||||
|
|
||||||
/** Registration identifier, needed by unregister to build message */
|
|
||||||
int _eXosipRegID;
|
|
||||||
|
|
||||||
/** Number of voicemail */
|
|
||||||
int _nMsgVoicemail;
|
|
||||||
|
|
||||||
/** when we init the listener, how many times we try to bind a port? */
|
/** when we init the listener, how many times we try to bind a port? */
|
||||||
int _nbTryListenAddr;
|
int _nbTryListenAddr;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class VoIPLink {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Virtual method
|
* Virtual method
|
||||||
* Try to initiate the eXosip engine/thread and set config
|
* Try to initiate the pjsip engine/thread and set config
|
||||||
* @return bool True if OK
|
* @return bool True if OK
|
||||||
*/
|
*/
|
||||||
virtual bool init (void) = 0;
|
virtual bool init (void) = 0;
|
||||||
|
Reference in New Issue
Block a user