/** * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Yan Morin <yan.morin@savoirfairelinux.com> * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (C) 2002,2003 Aymeric Moizard <jack@atosc.org> * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <eXosip2/eXosip.h> #include <osip2/osip.h> #include "sipvoiplink.h" #include "global.h" #include "audio/codecDescriptor.h" #include "manager.h" #include "sipcall.h" #include "user_cfg.h" #include "eventthread.h" #define DEFAULT_SIP_PORT 5060 #define RANDOM_SIP_PORT rand() % 64000 + 1024 #define DEFAULT_LOCAL_PORT 10500 #define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 #define VOICE_MSG "Voice-Message" #define LENGTH_VOICE_MSG 15 SipVoIPLink::SipVoIPLink() : VoIPLink() { // default _audioRTP object initialization _evThread = new EventThread(this); _localPort = 0; _nMsgVoicemail = 0; _reg_id = -1; // defautlt _sipcallVector object initialization _registrationSend = false; _started = false; } SipVoIPLink::~SipVoIPLink(void) { endSipCalls(); delete _evThread; _evThread = NULL; if (_started) { eXosip_quit(); } } // for voIPLink interface void SipVoIPLink::terminate(void) { } bool SipVoIPLink::checkNetwork (void) { // Set IP address return getSipLocalIp(); } int SipVoIPLink::init (void) { std::string tmp; tmp = std::string(PROGNAME) + "/" + std::string(SFLPHONED_VERSION); if (0 != eXosip_init()) { _debug("Could not initialize eXosip\n"); exit (0); } _started = true; srand (time(NULL)); //should be NULL or INADDR_ANY for the second parameter? int i; i = eXosip_listen_addr(IPPROTO_UDP, NULL, DEFAULT_SIP_PORT, AF_INET, 0); if (i != 0) { i = eXosip_listen_addr(IPPROTO_UDP, NULL, RANDOM_SIP_PORT, AF_INET, 0); if (i != 0) { _debug("Could not initialize transport layer\n"); return -1; } } // If use STUN server, firewall address setup if (Manager::instance().useStun()) { eXosip_set_user_agent(tmp.data()); if (behindNat() != 1) { return 0; } // This method is used to replace contact address with the public address of your NAT. eXosip_masquerade_contact((Manager::instance().getFirewallAddress()).data(), Manager::instance().getFirewallPort()); } else { // Set user agent eXosip_set_user_agent(tmp.data()); } _debug("SIP VoIP Link: listen to SIP Events\n"); _evThread->start(); return 1; } /** * Subscibe to message-summary notify * It allows eXosip to not send ' 481 Subcription Does Not Exist ' response */ void SipVoIPLink::subscribeMessageSummary() { osip_message_t *subscribe; const char *route= NULL; // from/to ManagerImpl& manager = Manager::instance(); std::string from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), manager.getConfigString(SIGNALISATION, HOST_PART)); // to std::string to; to = manager.getConfigString(SIGNALISATION, PROXY); if (!to.empty()) { to = toHeader(manager.getConfigString(SIGNALISATION, USER_PART)) + "@" + to; } else { to = from; } // like in http://www.faqs.org/rfcs/rfc3842.html const char *event="message-summary"; int expires = 86400; // return 0 if no error // the first from is the to... but we send the same int error = eXosip_subscribe_build_initial_request(&subscribe, to.c_str(), from.c_str(), route, event, expires); if (error == 0) { // Accept: application/simple-message-summary osip_message_set_header (subscribe, "Accept", "application/simple-message-summary"); _debug("Sending Message-summary subscription"); eXosip_lock(); // return 0 if ok error = eXosip_subscribe_send_initial_request (subscribe); _debug(" and return %d\n", error); eXosip_unlock(); } } bool SipVoIPLink::isInRtpmap (int index, int payload, CodecDescriptorVector* cdv) { for (int i = 0; i < index; i++) { if (cdv->at(i)->getPayload() == payload) { return true; } } return false; } int SipVoIPLink::setRegister (void) { ManagerImpl& manager = Manager::instance(); // all this will be inside the profil associate with the voip link std::string proxy = "sip:" + manager.getConfigString(SIGNALISATION, PROXY); std::string hostname = "sip:" + manager.getConfigString(SIGNALISATION, HOST_PART); std::string from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), manager.getConfigString(SIGNALISATION, HOST_PART)); if (manager.getConfigString(SIGNALISATION, HOST_PART).empty()) { manager.displayConfigError("Fill host part field"); return -1; } if (manager.getConfigString(SIGNALISATION, USER_PART).empty()) { manager.displayConfigError("Fill user part field"); return -1; } if (setAuthentication() == -1) { _debug("No authentication\n"); return -1; } _debug("REGISTER From: %s\n", from.data()); osip_message_t *reg = NULL; eXosip_lock(); if (!manager.getConfigString(SIGNALISATION, PROXY).empty()) { _reg_id = eXosip_register_build_initial_register ((char*)from.data(), (char*)proxy.data(), NULL, EXPIRES_VALUE, ®); } else { _reg_id = eXosip_register_build_initial_register ((char*)from.data(), (char*)hostname.data(), NULL, EXPIRES_VALUE, ®); } if (_reg_id < 0) { eXosip_unlock(); return -1; } osip_message_set_header (reg, "Event", "Registration"); osip_message_set_header (reg, "Allow-Events", "presence"); int i = eXosip_register_send_register (_reg_id, reg); if (i == -2) { _debug("cannot build registration, check the setup\n"); eXosip_unlock(); return -1; } if (i == -1) { _debug("Registration Failed\n"); eXosip_unlock(); return -1; } eXosip_unlock(); // subscribe to message one time? // subscribeMessageSummary(); _registrationSend = true; return i; } /** * setUnregister * unregister if we already send the first registration * @return -1 if there is an error */ int SipVoIPLink::setUnregister (void) { if ( _registrationSend ) { int i = 0; osip_message_t *reg = NULL; eXosip_lock(); if (_reg_id > 0) { _debug("UNREGISTER\n"); i = eXosip_register_build_register (_reg_id, 0, ®); } if (_reg_id < 0) { eXosip_unlock(); return -1; } i = eXosip_register_send_register (_reg_id, reg); if (i == -2) { _debug("(unregister) Cannot build registration, check the setup\n"); eXosip_unlock(); return -1; } if (i == -1) { _debug("(unregister) Registration Failed\n"); eXosip_unlock(); return -1; } eXosip_unlock(); return i; } else { // no registration send before return -1; } } int SipVoIPLink::outgoingInvite (CALLID id, const std::string& to_url) { std::string from; std::string to; // TODO: should be inside account settings ManagerImpl& manager = Manager::instance(); // Form the From header field basis on configuration panel std::string host = manager.getConfigString(SIGNALISATION, HOST_PART); if ( host.empty() ) { host = getLocalIpAddress(); } from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), host); to = toHeader(to_url); if (to.find("@") == std::string::npos and manager.getConfigInt(SIGNALISATION, AUTO_REGISTER)) { to = to + "@" + manager.getConfigString(SIGNALISATION, HOST_PART); } _debug("From: %s\n", from.data()); _debug("To: %s\n", to.data()); if (manager.getConfigString(SIGNALISATION, PROXY).empty()) { // If no SIP proxy setting for direct call with only IP address if (checkNetwork()) { if (startCall(id, from, to, "", "") <= 0) { _debug("Warning SipVoIPLink: call not started\n"); return -1; } } else { manager.displayErrorText(id, "No network found\n"); return -1; } return 0; } else { // If SIP proxy setting std::string route = "<sip:" + manager.getConfigString(SIGNALISATION, PROXY) + ";lr>"; if (checkNetwork()) { if (startCall(id, from, to, "", route) <= 0) { _debug("Warning SipVoIPLink: call not started\n"); return -1; } } else { manager.displayErrorText(id, "No network found\n"); return -1; } return 0; } } /** * @return 0 is good, -1 is bad */ int SipVoIPLink::answer (CALLID id) { int i; int port; char tmpbuf[64]; bzero (tmpbuf, 64); // Get port snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort()); _debug("Answer call [id = %d, cid = %d, did = %d]\n", id, getSipCall(id)->getCid(), getSipCall(id)->getDid()); port = getSipCall(id)->getLocalAudioPort(); _debug("Local audio port: %d\n", port); osip_message_t *answerMessage = NULL; SipCall* ca = getSipCall(id); // Send 180 RINGING eXosip_lock (); eXosip_call_send_answer (ca->getTid(), RINGING, NULL); eXosip_unlock (); // Send 200 OK eXosip_lock(); i = eXosip_call_build_answer (ca->getTid(), OK, &answerMessage); if (i != 0) { // Send 400 BAD_REQUEST eXosip_call_send_answer (ca->getTid(), BAD_REQ, NULL); } else { i = sdp_complete_200ok (ca->getDid(), answerMessage, port); if (i != 0) { osip_message_free (answerMessage); // Send 415 UNSUPPORTED_MEDIA_TYPE eXosip_call_send_answer (ca->getTid(), UNSUP_MEDIA_TYPE, NULL); } else { eXosip_call_send_answer (ca->getTid(), OK, answerMessage); } } eXosip_unlock(); // Incoming call is answered, start the sound channel. if (_audiortp.createNewSession (getSipCall(id)) < 0) { _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); i = -1; } return i; } /** * @return > 0 is good, -1 is bad */ int SipVoIPLink::hangup (CALLID id) { int i = 0; SipCall* sipcall = getSipCall(id); if (sipcall == NULL) { return -1; } _debug("Hang up call [id = %d, cid = %d, did = %d]\n", id, sipcall->getCid(), sipcall->getDid()); // Release SIP stack. eXosip_lock(); i = eXosip_call_terminate (sipcall->getCid(), sipcall->getDid()); eXosip_unlock(); // Release RTP channels _audiortp.closeRtpSession(); deleteSipCall(id); return i; } int SipVoIPLink::cancel (CALLID id) { int i = 0; SipCall* sipcall = getSipCall(id); _debug("Cancel call [id = %d, cid = %d]\n", id, sipcall->getCid()); // Release SIP stack. eXosip_lock(); i = eXosip_call_terminate (sipcall->getCid(), -1); eXosip_unlock(); deleteSipCall(id); return i; } /* * @return -1 = sipcall not present */ int SipVoIPLink::onhold (CALLID id) { osip_message_t *invite; int i; int did; sdp_message_t *local_sdp = NULL; SipCall *sipcall = getSipCall(id); if ( sipcall == NULL ) { return -1; } did = sipcall->getDid(); eXosip_lock (); local_sdp = eXosip_get_local_sdp (did); eXosip_unlock (); if (local_sdp == NULL) { return -1; } // Build INVITE_METHOD for put call on-hold eXosip_lock (); i = eXosip_call_build_request (did, INVITE_METHOD, &invite); eXosip_unlock (); if (i != 0) { sdp_message_free(local_sdp); return -1; } /* add sdp body */ { char *tmp = NULL; i = sdp_hold_call (local_sdp); if (i != 0) { sdp_message_free (local_sdp); osip_message_free (invite); return -1; } i = sdp_message_to_str (local_sdp, &tmp); sdp_message_free (local_sdp); if (i != 0) { osip_message_free (invite); osip_free (tmp); return -1; } osip_message_set_body (invite, tmp, strlen (tmp)); osip_free (tmp); osip_message_set_content_type (invite, "application/sdp"); } // Send request _audiortp.closeRtpSession(); eXosip_lock (); i = eXosip_call_send_request (did, invite); eXosip_unlock (); // Disable audio return i; } /** * @return 0 is good, -1 is bad */ int SipVoIPLink::offhold (CALLID id) { osip_message_t *invite; int i; int did; sdp_message_t *local_sdp = NULL; did = getSipCall(id)->getDid(); eXosip_lock (); local_sdp = eXosip_get_local_sdp (did); eXosip_unlock (); if (local_sdp == NULL) { return -1; } eXosip_lock (); // Build INVITE_METHOD for put call off-hold i = eXosip_call_build_request (did, INVITE_METHOD, &invite); eXosip_unlock (); if (i != 0) { sdp_message_free(local_sdp); return -1; } /* add sdp body */ { char *tmp = NULL; i = sdp_off_hold_call (local_sdp); if (i != 0) { sdp_message_free (local_sdp); osip_message_free (invite); return -1; } i = sdp_message_to_str (local_sdp, &tmp); sdp_message_free (local_sdp); if (i != 0) { osip_message_free (invite); osip_free (tmp); return -1; } osip_message_set_body (invite, tmp, strlen (tmp)); osip_free (tmp); osip_message_set_content_type (invite, "application/sdp"); } eXosip_lock (); // Send request i = eXosip_call_send_request (did, invite); eXosip_unlock (); // Enable audio if (_audiortp.createNewSession (getSipCall(id)) < 0) { _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); i = -1; } return i; } int SipVoIPLink::transfer (CALLID id, const std::string& to) { osip_message_t *refer; int i; std::string tmp_to; tmp_to = toHeader(to); if (tmp_to.find("@") == std::string::npos) { tmp_to = tmp_to + "@" + Manager::instance().getConfigString(SIGNALISATION, HOST_PART); } eXosip_lock(); // Build transfer request i = eXosip_call_build_refer (getSipCall(id)->getDid(), (char*)tmp_to.data(), &refer); if (i == 0) { // Send transfer request i = eXosip_call_send_request (getSipCall(id)->getDid(), refer); } eXosip_unlock(); return i; } int SipVoIPLink::refuse (CALLID id) { int i; char tmpbuf[64]; bzero (tmpbuf, 64); // Get local port snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort()); osip_message_t *answerMessage = NULL; eXosip_lock(); // not BUSY.. where decline the invitation! i = eXosip_call_build_answer (getSipCall(id)->getTid(), SIP_DECLINE, &answerMessage); if (i == 0) { i = eXosip_call_send_answer (getSipCall(id)->getTid(), SIP_DECLINE, answerMessage); } eXosip_unlock(); return i; } int SipVoIPLink::getEvent (void) { // wait for 0 s, 50 ms eXosip_event_t* event = eXosip_event_wait (0, 50); eXosip_lock(); eXosip_automatic_action(); eXosip_unlock(); if (event == NULL) { return -1; } SipCall* sipcall = NULL; CALLID id = 0; int returnValue = 0; _debug("Receive SipEvent #%d %s\n", event->type, event->textinfo); switch (event->type) { // IP-Phone user receives a new call case EXOSIP_CALL_INVITE: // _debug("> INVITE (receive)\n"); checkNetwork(); // Set local random port for incoming call if (!Manager::instance().useStun()) { setLocalPort(RANDOM_LOCAL_PORT); } else { // If there is a firewall if (behindNat() != 0) { setLocalPort(Manager::instance().getFirewallPort()); } else { returnValue = -1; break; } } // Generate id id = Manager::instance().generateNewCallId(); Manager::instance().pushBackNewCall(id, Incoming); _debug(" ID: %d [cid = %d, did = %d]\n",id, event->cid, event->did); // Associate an audio port with a call sipcall = getSipCall(id); sipcall->setLocalAudioPort(_localPort); sipcall->setLocalIp(getLocalIpAddress()); _debug(" Local listening port: %d\n", _localPort); _debug(" Local listening IP: %s\n", getLocalIpAddress().c_str()); sipcall->newIncomingCall(event); if (Manager::instance().incomingCall(id, sipcall->getName(), sipcall->getNumber()) < 0) { Manager::instance().displayErrorText(id, " Incoming Call Failed"); } break; case EXOSIP_CALL_REINVITE: _debug("!!! EXOSIP_CALL_REINVITE: Should reinvite? !!!\n"); //eXosip_call_send_answer(event->tid, 403, NULL); //488 as http://www.atosc.org/pipermail/public/osip/2005-June/005385.html id = findCallId(event); if (id != 0) { sipcall = getSipCall(id); if (sipcall != NULL) { _debug("Call reinvite : [id = %d, cid = %d, did = %d], localport=%d\n", id, event->cid, event->did,sipcall->getLocalAudioPort()); _audiortp.closeRtpSession(); sipcall->newIncomingCall(event); _audiortp.createNewSession(sipcall); } } else { eXosip_call_send_answer(event->tid, 488, NULL); } break; case EXOSIP_CALL_PROCEEDING: // 8 // proceeding call... break; case EXOSIP_CALL_RINGING: // 9 peer call is ringing id = findCallIdInitial(event); _debug("Call is ringing [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did); if (id != 0) { getSipCall(id)->ringingCall(event); Manager::instance().peerRingingCall(id); } else { returnValue = -1; } break; // The peer-user answers case EXOSIP_CALL_ANSWERED: // 10 { id = findCallIdInitial(event); if ( id != 0) { sipcall = getSipCall(id); if ( sipcall ) { _debug("Call is answered [id = %d, cid = %d, did = %d], localport=%d\n", id, event->cid, event->did,sipcall->getLocalAudioPort()); } // Answer if (Manager::instance().callCanBeAnswered(id)) { sipcall->setStandBy(false); _debug("Answering call first time\n"); if (sipcall->answeredCall(event) != -1) { sipcall->answeredCall_without_hold(event); Manager::instance().peerAnsweredCall(id); if(!Manager::instance().callIsOnHold(id)) { // Outgoing call is answered, start the sound channel. _debug("Starting AudioRTP\n"); if (_audiortp.createNewSession (sipcall) < 0) { _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); returnValue = -1; break; } } } } else { // Answer to on/off hold to send ACK _debug("Answering call\n"); sipcall->answeredCall(event); } break; } else { returnValue = -1; } } case EXOSIP_CALL_REDIRECTED: break; case EXOSIP_CALL_ACK: id = findCallId(event); _debug("ACK received [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did); if (id != 0) { getSipCall(id)->receivedAck(event); } else { returnValue = -1; } break; // The peer-user closed the phone call(we received BYE). case EXOSIP_CALL_CLOSED: id = findCallId(event); _debug("Call is closed [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did); if (id != 0) { if (Manager::instance().callCanBeClosed(id)) { sipcall = getSipCall(id); _audiortp.closeRtpSession(); } Manager::instance().peerHungupCall(id); deleteSipCall(id); } else { returnValue = -1; } break; case EXOSIP_CALL_RELEASED: //id = findCallId(event); //if (id!=0) { //Manager::instance().peerHungupCall(id); //deleteSipCall(id); //} break; case EXOSIP_CALL_REQUESTFAILURE: id = findCallId(event); // Handle 4XX errors switch (event->response->status_code) { case AUTH_REQUIRED: _debug("SIP Server ask required authentification: logging...\n"); setAuthentication(); eXosip_lock(); eXosip_automatic_action(); eXosip_unlock(); break; case UNAUTHORIZED: _debug("Request is unauthorized. SIP Server ask authentification: logging...\n"); setAuthentication(); break; case BAD_REQ: case FORBIDDEN: case NOT_FOUND: case NOT_ALLOWED: case NOT_ACCEPTABLE: case REQ_TIMEOUT: case TEMP_UNAVAILABLE: case ADDR_INCOMPLETE: case NOT_ACCEPTABLE_HERE: // 488 // Display error on the screen phone //Manager::instance().displayError(event->response->reason_phrase); Manager::instance().displayErrorText(id, event->response->reason_phrase); Manager::instance().callFailure(id); break; case BUSY_HERE: Manager::instance().displayErrorText(id, event->response->reason_phrase); Manager::instance().callBusy(id); break; case REQ_TERMINATED: break; default: break; } break; case EXOSIP_CALL_SERVERFAILURE: // Handle 5XX errors switch (event->response->status_code) { case SERVICE_UNAVAILABLE: id = findCallId(event); Manager::instance().callFailure(id); break; default: break; } break; case EXOSIP_CALL_GLOBALFAILURE: // Handle 6XX errors switch (event->response->status_code) { case BUSY_EVERYWHERE: case DECLINE: id = findCallId(event); Manager::instance().callFailure(id); break; default: break; } break; case EXOSIP_CALL_MESSAGE_NEW: // 18 // TODO: break; case EXOSIP_REGISTRATION_SUCCESS: // 1 // Manager::instance().displayStatus(LOGGED_IN_STATUS); break; case EXOSIP_REGISTRATION_FAILURE: // 2 //Manager::instance().displayError("getEvent : Registration Failure"); break; case EXOSIP_MESSAGE_NEW: unsigned int k; if (event->request != NULL && MSG_IS_OPTIONS(event->request)) { for (k = 0; k < _sipcallVector.size(); k++) { if (_sipcallVector.at(k)->getCid() == event->cid) { break; } } // TODO: Que faire si rien trouve?? eXosip_lock(); if (k == _sipcallVector.size()) { /* answer 200 ok */ eXosip_options_send_answer (event->tid, OK, NULL); } else if (_sipcallVector.at(k)->getCid() == event->cid) { /* already answered! */ } else { /* answer 486 ok */ eXosip_options_send_answer (event->tid, BUSY_HERE, NULL); } eXosip_unlock(); } // Voice message else if (event->request != NULL && MSG_IS_NOTIFY(event->request)){ int ii; unsigned int pos; unsigned int pos_slash; std::string nb_msg; osip_body_t *body = NULL; // Get the message body ii = osip_message_get_body(event->request, 0, &body); if (ii != 0) { _debug("Cannot get body in a new EXOSIP_MESSAGE_NEW event\n"); returnValue = -1; break; } // Analyse message body if (!body || !body->body) { returnValue = -1; break; } std::string str(body->body); pos = str.find(VOICE_MSG); if (pos == std::string::npos) { // If the string is not found returnValue = -1; break; } pos_slash = str.find ("/"); nb_msg = str.substr(pos + LENGTH_VOICE_MSG, pos_slash - (pos + LENGTH_VOICE_MSG)); // Set the number of voice-message setMsgVoicemail(atoi(nb_msg.data())); if (getMsgVoicemail() != 0) { // If there is at least one voice-message, start notification Manager::instance().startVoiceMessageNotification(nb_msg); } else { // Stop notification when there is 0 voice message Manager::instance().stopVoiceMessageNotification(); } } break; case EXOSIP_SUBSCRIPTION_ANSWERED: // 38 eXosip_lock(); eXosip_automatic_action(); eXosip_unlock(); break; case EXOSIP_SUBSCRIPTION_REQUESTFAILURE: //40 break; default: //Manager::instance().displayErrorText(event->type, "getEvent:default"); returnValue = -1; break; } //_debug(" : end event : %d / %d\n", event->type, returnValue); eXosip_event_free(event); return returnValue; } int SipVoIPLink::getLocalPort (void) { return _localPort; } void SipVoIPLink::setLocalPort (int port) { _localPort = port; } void SipVoIPLink::carryingDTMFdigits (CALLID id, char code) { int duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH); osip_message_t *info; const int body_len = 1000; int i; char *dtmf_body = new char[body_len]; eXosip_lock(); // Build info request i = eXosip_call_build_info (getSipCall(id)->getDid(), &info); if (i == 0) { snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration); osip_message_set_content_type (info, "application/dtmf-relay"); osip_message_set_body (info, dtmf_body, strlen (dtmf_body)); // Send info request i = eXosip_call_send_request (getSipCall(id)->getDid(), info); } eXosip_unlock(); delete[] dtmf_body; dtmf_body = NULL; } void SipVoIPLink::newOutgoingCall (CALLID id) { SipCall* sipcall = new SipCall(id, Manager::instance().getCodecDescVector()); if (sipcall != NULL) { _sipcallVector.push_back(sipcall); sipcall->setStandBy(true); } } void SipVoIPLink::newIncomingCall (CALLID id) { SipCall* sipcall = new SipCall(id, Manager::instance().getCodecDescVector()); if (sipcall != NULL) { _sipcallVector.push_back(sipcall); } } void SipVoIPLink::deleteSipCall (CALLID id) { std::vector< SipCall* >::iterator iter = _sipcallVector.begin(); while(iter != _sipcallVector.end()) { if (*iter && (*iter)->getId() == id) { delete *iter; *iter = NULL; _sipcallVector.erase(iter); return; } iter++; } } void SipVoIPLink::endSipCalls() { std::vector< SipCall* >::iterator iter = _sipcallVector.begin(); while(iter != _sipcallVector.end()) { if ( *iter ) { // Release SIP stack. eXosip_lock(); eXosip_call_terminate ((*iter)->getCid(), (*iter)->getDid()); eXosip_unlock(); // Release RTP channels _audiortp.closeRtpSession(); delete *iter; *iter = NULL; } iter++; } _sipcallVector.clear(); } SipCall* SipVoIPLink::getSipCall (CALLID id) { SipCall* sipcall = NULL; for (unsigned int i = 0; i < _sipcallVector.size(); i++) { sipcall = _sipcallVector.at(i); if (sipcall && sipcall->getId() == id) { return sipcall; } } return NULL; } AudioCodec* SipVoIPLink::getAudioCodec (CALLID id) { SipCall* sipcall = getSipCall(id); if (sipcall != NULL) { return sipcall->getAudioCodec(); } else { return NULL; } } /////////////////////////////////////////////////////////////////////////////// // Private functions /////////////////////////////////////////////////////////////////////////////// int SipVoIPLink::sdp_hold_call (sdp_message_t * sdp) { int pos; int pos_media = -1; char *rcvsnd; int recv_send = -1; pos = 0; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); while (rcvsnd != NULL) { if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly")) { recv_send = 0; } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly") || 0 == strcmp (rcvsnd, "sendrecv"))) { recv_send = 0; sprintf (rcvsnd, "sendonly"); } pos++; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); } pos_media = 0; while (!sdp_message_endof_media (sdp, pos_media)) { pos = 0; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); while (rcvsnd != NULL) { if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly")) { recv_send = 0; } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly") || 0 == strcmp (rcvsnd, "sendrecv"))) { recv_send = 0; sprintf (rcvsnd, "sendonly"); } pos++; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); } pos_media++; } if (recv_send == -1) { /* we need to add a global attribute with a field set to "sendonly" */ sdp_message_a_attribute_add (sdp, -1, osip_strdup ("sendonly"), NULL); } return 0; } int SipVoIPLink::sdp_off_hold_call (sdp_message_t * sdp) { int pos; int pos_media = -1; char *rcvsnd; pos = 0; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); while (rcvsnd != NULL) { if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly") || 0 == strcmp (rcvsnd, "recvonly"))) { sprintf (rcvsnd, "sendrecv"); } pos++; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); } pos_media = 0; while (!sdp_message_endof_media (sdp, pos_media)) { pos = 0; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); while (rcvsnd != NULL) { if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly") || 0 == strcmp (rcvsnd, "recvonly"))) { sprintf (rcvsnd, "sendrecv"); } pos++; rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos); } pos_media++; } return 0; } int SipVoIPLink::sdp_complete_200ok (int did, osip_message_t * answerMessage, int port) { sdp_message_t *remote_sdp; sdp_media_t *remote_med; char *tmp = NULL; char buf[4096]; char port_tmp[64]; int pos; char localip[128]; // Format port to a char* bzero(port_tmp, 64); snprintf(port_tmp, 63, "%d", port); remote_sdp = eXosip_get_remote_sdp (did); if (remote_sdp == NULL) { return -1; /* no existing body? */ } eXosip_guess_localip (AF_INET, localip, 128); 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", localip,localip); pos = 0; while (!osip_list_eol (remote_sdp->m_medias, pos)) { char payloads[128]; int pos2; memset (payloads, '\0', sizeof (payloads)); remote_med = (sdp_media_t *) osip_list_get (remote_sdp->m_medias, pos); if (0 == osip_strcasecmp (remote_med->m_media, "audio")) { pos2 = 0; while (!osip_list_eol (remote_med->m_payloads, pos2)) { tmp = (char *) osip_list_get (remote_med->m_payloads, pos2); if (tmp != NULL && (0 == osip_strcasecmp (tmp, "0") || 0 == osip_strcasecmp (tmp, "8") || 0 == osip_strcasecmp (tmp, "3"))) { strcat (payloads, tmp); strcat (payloads, " "); } pos2++; } strcat (buf, "m="); strcat (buf, remote_med->m_media); if (pos2 == 0 || payloads[0] == '\0') { strcat (buf, " 0 RTP/AVP \r\n"); sdp_message_free (remote_sdp); return -1; /* refuse anyway */ } else { strcat (buf, " "); strcat (buf, port_tmp); strcat (buf, " RTP/AVP "); strcat (buf, payloads); strcat (buf, "\r\n"); if (NULL != strstr (payloads, " 0 ") || (payloads[0] == '0' && payloads[1] == ' ')) { strcat (buf, "a=rtpmap:0 PCMU/8000\r\n"); } if (NULL != strstr (payloads, " 8 ") || (payloads[0] == '8' && payloads[1] == ' ')) { strcat (buf, "a=rtpmap:8 PCMA/8000\r\n"); } if (NULL != strstr (payloads, " 3") || (payloads[0] == '3' && payloads[1] == ' ')) { strcat (buf, "a=rtpmap:3 GSM/8000\r\n"); } } } else { strcat (buf, "m="); strcat (buf, remote_med->m_media); strcat (buf, " 0 "); strcat (buf, remote_med->m_proto); strcat (buf, " \r\n"); } pos++; } osip_message_set_body (answerMessage, buf, strlen (buf)); osip_message_set_content_type (answerMessage, "application/sdp"); sdp_message_free (remote_sdp); return 0; } int SipVoIPLink::behindNat (void) { StunAddress4 stunSvrAddr; stunSvrAddr.addr = 0; // Stun server std::string svr = Manager::instance().getConfigString(SIGNALISATION, STUN_SERVER); // Convert char* to StunAddress4 structure bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr); if (!ret) { _debug("SIP: Stun server address (%s) is not valid\n", svr.data()); return 0; } // Firewall address //_debug("STUN server: %s\n", svr.data()); Manager::instance().getStunInfo(stunSvrAddr); return 1; } /** * Get the local Ip by eXosip * only if the local ip address is to his default value: 127.0.0.1 * setLocalIpAdress * @return false if not found */ bool SipVoIPLink::getSipLocalIp (void) { bool returnValue = true; if (getLocalIpAddress() == "127.0.0.1") { char* myIPAddress = new char[65]; if (eXosip_guess_localip (AF_INET, myIPAddress, 64) == -1) { returnValue = false; } setLocalIpAddress(std::string(myIPAddress)); delete [] myIPAddress; myIPAddress = NULL; } return returnValue; } int SipVoIPLink::checkUrl (const std::string& url) { int i; osip_from_t *to; i = osip_from_init(&to); if (i != 0) { _debug("Warning: Cannot initialize\n"); return -1; } i = osip_from_parse(to, url.data()); if (i != 0) { _debug("Warning: Cannot parse url\n"); return -1; } // Free memory osip_from_free (to); return 0; } int SipVoIPLink::setAuthentication (void) { ManagerImpl& manager = Manager::instance(); std::string login, pass, realm; login = manager.getConfigString(SIGNALISATION, AUTH_USER_NAME); if (login.empty()) { login = manager.getConfigString(SIGNALISATION, USER_PART); } pass = manager.getConfigString(SIGNALISATION, PASSWORD); if (pass.empty()) { manager.displayConfigError("Fill password field"); return -1; } int returnValue = 0; eXosip_lock(); if (eXosip_add_authentication_info(login.data(), login.data(), pass.data(), NULL, NULL) != 0) { returnValue = -1; } eXosip_unlock(); return returnValue; } std::string SipVoIPLink::fromHeader (const std::string& user, const std::string& host) { std::string displayname = Manager::instance().getConfigString(SIGNALISATION, FULL_NAME); return ("\"" + displayname + "\"" + " <sip:" + user + "@" + host + ">"); } std::string SipVoIPLink::toHeader(const std::string& to) { if (to.find("sip:") == std::string::npos) { return ("sip:" + to ); } else { return to; } } int SipVoIPLink::startCall (CALLID id, const std::string& from, const std::string& to, const std::string& subject, const std::string& route) { SipCall* sipcall = getSipCall(id); if ( sipcall == NULL) { return -1; // error, we can't find the sipcall } osip_message_t *invite; if (checkUrl(from) != 0) { Manager::instance().displayConfigError("Error in source address"); return -1; } if (checkUrl(to) != 0) { Manager::instance().displayErrorText(id, "Error in destination address"); return -1; } if (!Manager::instance().useStun()) { // Set random port for outgoing call if no firewall setLocalPort(RANDOM_LOCAL_PORT); _debug("SipVoIPLink::startCall: Local audio port: %d\n",_localPort); } else { // If use Stun server if (behindNat() != 0) { _debug("SipVoIPLink::startCall: sip invite: firewall port = %d\n",Manager::instance().getFirewallPort()); setLocalPort(Manager::instance().getFirewallPort()); } else { return -1; } } // Set local audio port for sipcall(id) sipcall->setLocalAudioPort(_localPort); sipcall->setLocalIp(getLocalIpAddress()); int i = eXosip_call_build_initial_invite (&invite, (char*)to.data(), (char*)from.data(), (char*)route.data(), (char*)subject.data()); if (i != 0) { return -1; // error when building the invite } int payload; char rtpmap[128]; char rtpmap_attr[2048]; char media[64]; char media_audio[64]; bzero(rtpmap, 128); bzero(rtpmap_attr, 2048); bzero(media, 64); bzero(media_audio, 64); // Set rtpmap according to the supported codec order CodecDescriptorVector* cdv = Manager::instance().getCodecDescVector(); unsigned int nb = cdv->size(); for (unsigned int iCodec = 0; iCodec < nb; iCodec++) { payload = cdv->at(iCodec)->getPayload(); // Add payload to rtpmap if it is not already added if (!isInRtpmap(iCodec, payload, cdv)) { snprintf(media, 63, "%d ", payload); strcat (media_audio, media); snprintf(rtpmap, 127, "a=rtpmap: %d %s/%d\r\n", payload, cdv->at(iCodec)->rtpmapPayload(payload).data(), SAMPLING_RATE); strcat(rtpmap_attr, rtpmap); } } // http://www.antisip.com/documentation/eXosip2/group__howto1__initialize.html // tell sip if we support SIP extension like 100rel // osip_message_set_supported (invite, "100rel"); /* add sdp body */ { char tmp[4096]; char localip[128]; eXosip_guess_localip (AF_INET, localip, 128); char port[64]; bzero (port, 64); snprintf (port, 63, "%d", getLocalPort()); snprintf (tmp, 4096, "v=0\r\n" "o=SFLphone 0 0 IN IP4 %s\r\n" "s=call\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %s RTP/AVP %s\r\n" "%s", localip, localip, port, media_audio, rtpmap_attr); // media_audio should be one, two or three numbers? osip_message_set_body (invite, tmp, strlen (tmp)); osip_message_set_content_type (invite, "application/sdp"); } eXosip_lock(); // this is the cid (call id from exosip) int cid = eXosip_call_send_initial_invite (invite); // Keep the cid in case of cancelling sipcall->setCid(cid); if (cid <= 0) { eXosip_unlock(); return -1; } else { eXosip_call_set_reference (cid, NULL); } eXosip_unlock(); return cid; // this is the Cid } CALLID SipVoIPLink::findCallId (eXosip_event_t *e) { for (unsigned int k = 0; k < _sipcallVector.size(); k++) { SipCall* sipcall = _sipcallVector.at(k); if (sipcall && sipcall->getCid() == e->cid && sipcall->getDid() == e->did) { return sipcall->getId(); } } return 0; } /** * This function is used when findCallId failed (return 0) * ie: the dialog id change * can be use when anwsering a new call or * when cancelling a call */ CALLID SipVoIPLink::findCallIdInitial (eXosip_event_t *e) { for (unsigned int k = 0; k < _sipcallVector.size(); k++) { SipCall* sipcall = _sipcallVector.at(k); // the dialog id is not set when you do a new call // so you can't check it when you want to retreive it // for the first call anwser if (sipcall && sipcall->getCid() == e->cid) { return sipcall->getId(); } } return 0; }