Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see
*
*/
package org.restcomm.connect.telephony;
import akka.actor.ActorContext;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.ReceiveTimeout;
import akka.actor.StopChild;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorContext;
import akka.actor.UntypedActorFactory;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.util.Timeout;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import gov.nist.javax.sip.header.UserAgent;
import org.apache.commons.configuration.Configuration;
import org.joda.time.DateTime;
import org.restcomm.connect.commons.configuration.RestcommConfiguration;
import org.restcomm.connect.commons.dao.Sid;
import org.restcomm.connect.commons.patterns.StopObserving;
import org.restcomm.connect.commons.util.SdpUtils;
import org.restcomm.connect.commons.util.UriUtils;
import org.restcomm.connect.dao.AccountsDao;
import org.restcomm.connect.dao.ApplicationsDao;
import org.restcomm.connect.dao.CallDetailRecordsDao;
import org.restcomm.connect.dao.ClientsDao;
import org.restcomm.connect.dao.DaoManager;
import org.restcomm.connect.dao.IncomingPhoneNumbersDao;
import org.restcomm.connect.dao.NotificationsDao;
import org.restcomm.connect.dao.RegistrationsDao;
import org.restcomm.connect.dao.entities.Account;
import org.restcomm.connect.dao.entities.Application;
import org.restcomm.connect.dao.entities.CallDetailRecord;
import org.restcomm.connect.dao.entities.Client;
import org.restcomm.connect.dao.entities.IncomingPhoneNumber;
import org.restcomm.connect.dao.entities.Notification;
import org.restcomm.connect.dao.entities.Registration;
import org.restcomm.connect.extension.api.CallRequest;
import org.restcomm.connect.extension.api.ExtensionResponse;
import org.restcomm.connect.extension.api.ExtensionType;
import org.restcomm.connect.extension.api.RestcommExtensionException;
import org.restcomm.connect.extension.api.RestcommExtensionGeneric;
import org.restcomm.connect.extension.controller.ExtensionController;
import org.restcomm.connect.interpreter.StartInterpreter;
import org.restcomm.connect.interpreter.StopInterpreter;
import org.restcomm.connect.interpreter.VoiceInterpreterBuilder;
import org.restcomm.connect.monitoringservice.MonitoringService;
import org.restcomm.connect.mscontrol.api.MediaServerControllerFactory;
import org.restcomm.connect.telephony.api.CallInfo;
import org.restcomm.connect.telephony.api.CallManagerResponse;
import org.restcomm.connect.telephony.api.CallResponse;
import org.restcomm.connect.telephony.api.CallStateChanged;
import org.restcomm.connect.telephony.api.CreateCall;
import org.restcomm.connect.telephony.api.DestroyCall;
import org.restcomm.connect.telephony.api.ExecuteCallScript;
import org.restcomm.connect.telephony.api.GetActiveProxy;
import org.restcomm.connect.telephony.api.GetCall;
import org.restcomm.connect.telephony.api.GetCallInfo;
import org.restcomm.connect.telephony.api.GetCallObservers;
import org.restcomm.connect.telephony.api.GetProxies;
import org.restcomm.connect.telephony.api.GetRelatedCall;
import org.restcomm.connect.telephony.api.Hangup;
import org.restcomm.connect.telephony.api.InitializeOutbound;
import org.restcomm.connect.telephony.api.SwitchProxy;
import org.restcomm.connect.telephony.api.UpdateCallScript;
import org.restcomm.connect.telephony.api.util.B2BUAHelper;
import org.restcomm.connect.telephony.api.util.CallControlHelper;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import javax.sdp.SdpParseException;
import javax.servlet.ServletContext;
import javax.servlet.sip.Address;
import javax.servlet.sip.AuthInfo;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipApplicationSessionEvent;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TelURL;
import javax.sip.header.RouteHeader;
import javax.sip.message.Response;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import static akka.pattern.Patterns.ask;
import static javax.servlet.sip.SipServlet.OUTBOUND_INTERFACES;
import static javax.servlet.sip.SipServletResponse.SC_ACCEPTED;
import static javax.servlet.sip.SipServletResponse.SC_BAD_REQUEST;
import static javax.servlet.sip.SipServletResponse.SC_FORBIDDEN;
import static javax.servlet.sip.SipServletResponse.SC_NOT_FOUND;
import static javax.servlet.sip.SipServletResponse.SC_OK;
import static javax.servlet.sip.SipServletResponse.SC_SERVER_INTERNAL_ERROR;
/**
* @author [email protected] (Thomas Quintana)
* @author [email protected]
* @author [email protected]
* @author [email protected]
*/
public final class CallManager extends UntypedActor {
static final int ERROR_NOTIFICATION = 0;
static final int WARNING_NOTIFICATION = 1;
static final Pattern PATTERN = Pattern.compile("[\\*#0-9]{1,12}");
static final String EMAIL_SENDER = "[email protected]";
static final String EMAIL_SUBJECT = "RestComm Error Notification - Attention Required";
static final int DEFAUL_IMS_PROXY_PORT = -1;
private final ActorSystem system;
private final ActorRef supervisor;
private final Configuration configuration;
private final ServletContext context;
private final MediaServerControllerFactory msControllerFactory;
private final ActorRef conferences;
private final ActorRef bridges;
private final ActorRef sms;
private final SipFactory sipFactory;
private final DaoManager storage;
private final ActorRef monitoring;
// configurable switch whether to use the To field in a SIP header to determine the callee address
// alternatively the Request URI can be used
private boolean useTo;
private boolean authenticateUsers;
private AtomicInteger numberOfFailedCalls;
private AtomicBoolean useFallbackProxy;
private boolean allowFallback;
private boolean allowFallbackToPrimary;
private int maxNumberOfFailedCalls;
private String primaryProxyUri;
private String primaryProxyUsername, primaryProxyPassword;
private String fallBackProxyUri;
private String fallBackProxyUsername, fallBackProxyPassword;
private String activeProxy;
private String activeProxyUsername, activeProxyPassword;
private String mediaExternalIp;
private String myHostIp;
private String proxyIp;
private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
private CreateCall createCallRequest;
private SwitchProxy switchProxyRequest;
//Control whether Restcomm will patch Request-URI and SDP for B2BUA calls
private boolean patchForNatB2BUASessions;
//List of extensions for CallManager
List extensions;
// IMS authentication
private boolean actAsImsUa;
private String imsProxyAddress;
private int imsProxyPort;
private String imsDomain;
private String imsAccount;
// used for sending warning and error logs to notification engine and to the console
private void sendNotification(String errMessage, int errCode, String errType, boolean createNotification) {
NotificationsDao notifications = storage.getNotificationsDao();
Notification notification;
if (errType == "warning") {
if(logger.isDebugEnabled()) {
// https://github.com/RestComm/Restcomm-Connect/issues/1419 moved to debug to avoid polluting logs
logger.debug(errMessage); // send message to console
}
if (createNotification) {
notification = notification(ERROR_NOTIFICATION, errCode, errMessage);
notifications.addNotification(notification);
}
} else if (errType == "error") {
// https://github.com/RestComm/Restcomm-Connect/issues/1419 moved to debug to avoid polluting logs
if(logger.isDebugEnabled()) {
logger.debug(errMessage); // send message to console
}
if (createNotification) {
notification = notification(ERROR_NOTIFICATION, errCode, errMessage);
notifications.addNotification(notification);
}
} else if (errType == "info") {
// https://github.com/RestComm/Restcomm-Connect/issues/1419 moved to debug to avoid polluting logs
if(logger.isDebugEnabled()) {
logger.debug(errMessage); // send message to console
}
}
}
public CallManager(final Configuration configuration, final ServletContext context, final ActorSystem system, final ActorRef supervisor,
final MediaServerControllerFactory msControllerFactory, final ActorRef conferences, final ActorRef bridges,
final ActorRef sms, final SipFactory factory, final DaoManager storage) {
super();
this.system = system;
this.supervisor = supervisor;
this.configuration = configuration;
this.context = context;
this.msControllerFactory = msControllerFactory;
this.conferences = conferences;
this.bridges = bridges;
this.sms = sms;
this.sipFactory = factory;
this.storage = storage;
final Configuration runtime = configuration.subset("runtime-settings");
final Configuration outboundProxyConfig = runtime.subset("outbound-proxy");
SipURI outboundIntf = outboundInterface("udp");
if (outboundIntf != null) {
myHostIp = ((SipURI) outboundIntf).getHost().toString();
} else {
String errMsg = "SipURI outboundIntf is null";
sendNotification(errMsg, 14001, "error", false);
if (context == null)
errMsg = "SipServlet context is null";
sendNotification(errMsg, 14002, "error", false);
}
Configuration mediaConf = configuration.subset("media-server-manager");
mediaExternalIp = mediaConf.getString("mgcp-server.external-address");
proxyIp = runtime.subset("telestax-proxy").getString("uri").replaceAll("http://", "").replaceAll(":2080", "");
if (mediaExternalIp == null || mediaExternalIp.isEmpty())
mediaExternalIp = myHostIp;
if (proxyIp == null || proxyIp.isEmpty())
proxyIp = myHostIp;
this.useTo = runtime.getBoolean("use-to");
this.authenticateUsers = runtime.getBoolean("authenticate");
this.primaryProxyUri = outboundProxyConfig.getString("outbound-proxy-uri");
this.primaryProxyUsername = outboundProxyConfig.getString("outbound-proxy-user");
this.primaryProxyPassword = outboundProxyConfig.getString("outbound-proxy-password");
this.fallBackProxyUri = outboundProxyConfig.getString("fallback-outbound-proxy-uri");
this.fallBackProxyUsername = outboundProxyConfig.getString("fallback-outbound-proxy-user");
this.fallBackProxyPassword = outboundProxyConfig.getString("fallback-outbound-proxy-password");
this.activeProxy = primaryProxyUri;
this.activeProxyUsername = primaryProxyUsername;
this.activeProxyPassword = primaryProxyPassword;
numberOfFailedCalls = new AtomicInteger();
numberOfFailedCalls.set(0);
useFallbackProxy = new AtomicBoolean();
useFallbackProxy.set(false);
allowFallback = outboundProxyConfig.getBoolean("allow-fallback", false);
maxNumberOfFailedCalls = outboundProxyConfig.getInt("max-failed-calls", 20);
allowFallbackToPrimary = outboundProxyConfig.getBoolean("allow-fallback-to-primary", false);
patchForNatB2BUASessions = runtime.getBoolean("patch-for-nat-b2bua-sessions", true);
//Monitoring Service
this.monitoring = (ActorRef) context.getAttribute(MonitoringService.class.getName());
extensions = ExtensionController.getInstance().getExtensions(ExtensionType.CallManager);
if (logger.isInfoEnabled()) {
logger.info("CallManager extensions: "+(extensions != null ? extensions.size() : "0"));
}
if(!runtime.subset("ims-authentication").isEmpty()){
final Configuration imsAuthentication = runtime.subset("ims-authentication");
this.actAsImsUa = imsAuthentication.getBoolean("act-as-ims-ua");
if (actAsImsUa) {
this.imsProxyAddress = imsAuthentication.getString("proxy-address");
this.imsProxyPort = imsAuthentication.getInt("proxy-port");
if (imsProxyPort == 0) {
imsProxyPort = DEFAUL_IMS_PROXY_PORT;
}
this.imsDomain = imsAuthentication.getString("domain");
this.imsAccount = imsAuthentication.getString("account");
if (actAsImsUa && (imsProxyAddress == null || imsProxyAddress.isEmpty()
|| imsDomain == null || imsDomain.isEmpty())) {
logger.warning("ims proxy-address or domain is not configured");
}
this.actAsImsUa = actAsImsUa && imsProxyAddress != null && !imsProxyAddress.isEmpty()
&& imsDomain != null && !imsDomain.isEmpty();
}
}
firstTimeCleanup();
}
private void firstTimeCleanup() {
if (logger.isInfoEnabled())
logger.info("Initial CallManager cleanup. Will check running state calls in DB and update state of the calls.");
String instanceId = RestcommConfiguration.getInstance().getMain().getInstanceId();
Sid sid = new Sid(instanceId);
final CallDetailRecordsDao callDetailRecordsDao = storage.getCallDetailRecordsDao();
callDetailRecordsDao.updateInCompleteCallDetailRecordsToCompletedByInstanceId(sid);
List results = callDetailRecordsDao.getInCompleteCallDetailRecordsByInstanceId(sid);
if (logger.isInfoEnabled()) {
logger.info("There are: " + results.size() + " calls in progress after cleanup.");
}
}
private ActorRef call() {
final Props props = new Props(new UntypedActorFactory() {
private static final long serialVersionUID = 1L;
@Override
public UntypedActor create() throws Exception {
return new Call(sipFactory, msControllerFactory.provideCallController(), configuration);
}
});
ActorRef call = null;
try {
call = (ActorRef) Await.result(ask(supervisor, props, 500), Duration.create(500, TimeUnit.MILLISECONDS));
} catch (Exception e) {
logger.error("Problem during creation of actor: "+e);
}
return call;
}
private void check(final Object message) throws IOException {
final SipServletRequest request = (SipServletRequest) message;
String content = new String(request.getRawContent());
if (request.getContentLength() == 0
|| !("application/sdp".equals(request.getContentType()) || content.contains("application/sdp"))) {
final SipServletResponse response = request.createResponse(SC_BAD_REQUEST);
response.send();
}
}
private void destroy(final Object message) throws Exception {
final UntypedActorContext context = getContext();
final DestroyCall request = (DestroyCall) message;
ActorRef call = request.call();
if (call != null) {
if(logger.isInfoEnabled()) {
logger.info("About to destroy call: "+request.call().path()+", call isTerminated(): "+sender().isTerminated()+", sender: "+sender());
}
supervisor.tell(new StopChild(call), null);
}
}
private void invite(final Object message) throws IOException, NumberParseException, ServletParseException {
final ActorRef self = self();
final SipServletRequest request = (SipServletRequest) message;
// Make sure we handle re-invites properly.
if (!request.isInitial()) {
SipApplicationSession appSession = request.getApplicationSession();
ActorRef call = null;
if (appSession.getAttribute(Call.class.getName()) != null) {
call = (ActorRef) appSession.getAttribute(Call.class.getName());
}
if (call != null) {
if (logger.isInfoEnabled()) {
logger.info("For In-Dialog INVITE dispatched to Call actor: "+call.path());
}
call.tell(request, self);
return;
}
if (logger.isInfoEnabled()) {
logger.info("No call actor found will respond 200OK for In-dialog INVITE: "+request.getRequestURI().toString());
}
final SipServletResponse okay = request.createResponse(SC_OK);
okay.send();
return;
}
if (actAsImsUa) {
boolean isFromIms = isFromIms(request);
if (!isFromIms) {
//This is a WebRTC client that dials out to IMS
String user = request.getHeader("X-RestComm-Ims-User");
String pass = request.getHeader("X-RestComm-Ims-Password");
request.removeHeader("X-RestComm-Ims-User");
request.removeHeader("X-RestComm-Ims-Password");
imsProxyThroughMediaServer(request, null, request.getTo().getURI(), user, pass, isFromIms);
return;
} else {
//This is a IMS that dials out to WebRTC client
imsProxyThroughMediaServer(request, null, request.getTo().getURI(), "", "", isFromIms);
return;
}
}
//Run proInboundAction Extensions here
// If it's a new invite lets try to handle it.
final AccountsDao accounts = storage.getAccountsDao();
final ApplicationsDao applications = storage.getApplicationsDao();
// Try to find an application defined for the client.
final SipURI fromUri = (SipURI) request.getFrom().getURI();
String fromUser = fromUri.getUser();
final ClientsDao clients = storage.getClientsDao();
final Client client = clients.getClient(fromUser);
if (client != null) {
// Make sure we force clients to authenticate.
if (!authenticateUsers // https://github.com/Mobicents/RestComm/issues/29 Allow disabling of SIP authentication
|| CallControlHelper.checkAuthentication(request, storage)) {
// if the client has authenticated, try to redirect to the Client VoiceURL app
// otherwise continue trying to process the Client invite
if (redirectToClientVoiceApp(self, request, accounts, applications, client)) {
return;
} // else continue trying other ways to handle the request
} else {
// Since the client failed to authenticate, we will take no further action at this time.
return;
}
}
// TODO Enforce some kind of security check for requests coming from outside SIP UAs such as ITSPs that are not
// registered
final String toUser = CallControlHelper.getUserSipId(request, useTo);
final String ruri = ((SipURI) request.getRequestURI()).getHost();
final String toHost = ((SipURI) request.getTo().getURI()).getHost();
final String toHostIpAddress = InetAddress.getByName(toHost).getHostAddress();
final String toPort = String.valueOf(((SipURI) request.getTo().getURI()).getPort()).equalsIgnoreCase("-1") ? "5060"
: String.valueOf(((SipURI) request.getTo().getURI()).getHost());
final String transport = ((SipURI) request.getTo().getURI()).getTransportParam() == null ? "udp" : ((SipURI) request
.getTo().getURI()).getTransportParam();
SipURI outboundIntf = outboundInterface(transport);
if(logger.isInfoEnabled()) {
logger.info("ToHost: " + toHost);
logger.info("ruri: " + ruri);
logger.info("myHostIp: " + myHostIp);
logger.info("mediaExternalIp: " + mediaExternalIp);
logger.info("proxyIp: " + proxyIp);
}
if (client != null) { // make sure the caller is a registered client and not some external SIP agent that we have little control over
Client toClient = clients.getClient(toUser);
if (toClient != null) { // looks like its a p2p attempt between two valid registered clients, lets redirect to the b2bua
if(logger.isInfoEnabled()) {
logger.info("Client is not null: " + client.getLogin() + " will try to proxy to client: "+ toClient);
}
CallRequest callRequest = new CallRequest(fromUser, toUser, CallRequest.Type.CLIENT,
client.getAccountSid(), false, false);
if (executePreOutboundAction(callRequest)) {
if (B2BUAHelper.redirectToB2BUA(request, client, toClient, storage, sipFactory, patchForNatB2BUASessions)) {
if(logger.isInfoEnabled()) {
logger.info("Call to CLIENT. myHostIp: " + myHostIp + " mediaExternalIp: " + mediaExternalIp + " toHost: "
+ toHost + " fromClient: " + client.getUri() + " toClient: " + toClient.getUri());
}
// if all goes well with proxying the invitation on to the next client
// then we can end further processing of this INVITE
} else {
String errMsg = "Cannot Connect to Client: " + toClient.getFriendlyName()
+ " : Make sure the Client exist or is registered with Restcomm";
sendNotification(errMsg, 11001, "warning", true);
final SipServletResponse resp = request.createResponse(SC_NOT_FOUND, "Cannot complete P2P call");
resp.send();
}
} else {
//Extensions didn't allowed this call
if (logger.isDebugEnabled()) {
final String errMsg = "Client not Allowed to make this outbound call";
logger.debug(errMsg);
}
String errMsg = "Cannot Connect to Client: " + toClient.getFriendlyName()
+ " : Make sure the Client exist or is registered with Restcomm";
sendNotification(errMsg, 11001, "warning", true);
final SipServletResponse resp = request.createResponse(SC_FORBIDDEN, "Call not allowed");
resp.send();
}
executePostOutboundAction(callRequest);
return;
} else {
// toClient is null or we couldn't make the b2bua call to another client. check if this call is for a registered
// DID (application)
if (redirectToHostedVoiceApp(self, request, accounts, applications, toUser)) {
// This is a call to a registered DID (application)
return;
}
// This call is not a registered DID (application). Try to proxy out this call.
// log to console and to notification engine
String errMsg = "A Restcomm Client is trying to call a Number/DID that is not registered with Restcomm";
sendNotification(errMsg, 11002, "info", true);
if (isWebRTC(request)) {
//This is a WebRTC client that dials out
proxyThroughMediaServer(request, client, toUser);
return;
}
// https://telestax.atlassian.net/browse/RESTCOMM-335
final String proxyURI = activeProxy;
final String proxyUsername = activeProxyUsername;
final String proxyPassword = activeProxyPassword;
SipURI from = null;
SipURI to = null;
boolean callToSipUri = false;
// proxy DID or number if the outbound proxy fields are not empty in the restcomm.xml
if (proxyURI != null && !proxyURI.isEmpty()) {
// String destination = ((SipURI)request.getTo().getURI()).getUser();
CallRequest callRequest = new CallRequest(fromUser,toUser, CallRequest.Type.PSTN, client.getAccountSid(), false, false);
if (executePreOutboundAction(callRequest)) {
proxyOut(request, client, toUser, toHost, toHostIpAddress, toPort, outboundIntf, proxyURI, proxyUsername, proxyPassword, from, to, callToSipUri);
} else {
final SipServletResponse response = request.createResponse(SC_FORBIDDEN, "Call request not allowed");
response.send();
if (logger.isDebugEnabled()) {
logger.debug("Call request now allowed: "+callRequest.toString());
}
}
executePostOutboundAction(callRequest);
return;
} else {
String msg = "Restcomm tried to proxy this call to an outbound party but it seems the outbound proxy is not configured.";
sendNotification(errMsg, 11004, "warning", true);
}
}
} else {
// Client is null, check if this call is for a registered DID (application)
if (redirectToHostedVoiceApp(self, request, accounts, applications, toUser)) {
// This is a call to a registered DID (application)
return;
}
}
final SipServletResponse response = request.createResponse(SC_NOT_FOUND);
response.send();
// We didn't find anyway to handle the call.
String errMsg = "Restcomm cannot process this call because the destination number " + toUser
+ "cannot be found or there is application attached to that";
sendNotification(errMsg, 11005, "error", true);
}
private boolean proxyOut(SipServletRequest request, Client client, String toUser, String toHost, String toHostIpAddress, String toPort, SipURI outboundIntf, String proxyURI, String proxyUsername, String proxyPassword, SipURI from, SipURI to, boolean callToSipUri) throws UnknownHostException {
final Configuration runtime = configuration.subset("runtime-settings");
final boolean useLocalAddressAtFromHeader = runtime.getBoolean("use-local-address", false);
final boolean outboudproxyUserAtFromHeader = runtime.subset("outbound-proxy").getBoolean(
"outboudproxy-user-at-from-header", true);
final String fromHost = ((SipURI) request.getFrom().getURI()).getHost();
final String fromHostIpAddress = InetAddress.getByName(fromHost).getHostAddress();
// final String fromPort = String.valueOf(((SipURI) request.getFrom().getURI()).getPort()).equalsIgnoreCase("-1") ? "5060"
// : String.valueOf(((SipURI) request.getFrom().getURI()).getHost());
if(logger.isInfoEnabled()) {
logger.info("fromHost: " + fromHost + "fromHostIP: " + fromHostIpAddress + "myHostIp: " + myHostIp + " mediaExternalIp: " + mediaExternalIp
+ " toHost: " + toHost + " toHostIP: " + toHostIpAddress + " proxyUri: " + proxyURI);
}
if ((myHostIp.equalsIgnoreCase(toHost) || mediaExternalIp.equalsIgnoreCase(toHost)) ||
(myHostIp.equalsIgnoreCase(toHostIpAddress) || mediaExternalIp.equalsIgnoreCase(toHostIpAddress))
// https://github.com/RestComm/Restcomm-Connect/issues/1357
|| (fromHost.equalsIgnoreCase(toHost) || fromHost.equalsIgnoreCase(toHostIpAddress))
|| (fromHostIpAddress.equalsIgnoreCase(toHost) || fromHostIpAddress.equalsIgnoreCase(toHostIpAddress))) {
if(logger.isInfoEnabled()) {
logger.info("Call to NUMBER. myHostIp: " + myHostIp + " mediaExternalIp: " + mediaExternalIp
+ " toHost: " + toHost + " proxyUri: " + proxyURI);
}
try {
if (useLocalAddressAtFromHeader) {
if (outboudproxyUserAtFromHeader) {
from = (SipURI) sipFactory.createSipURI(proxyUsername,
mediaExternalIp + ":" + outboundIntf.getPort());
} else {
from = sipFactory.createSipURI(((SipURI) request.getFrom().getURI()).getUser(),
mediaExternalIp + ":" + outboundIntf.getPort());
}
} else {
if (outboudproxyUserAtFromHeader) {
// https://telestax.atlassian.net/browse/RESTCOMM-633. Use the outbound proxy username as
// the userpart of the sip uri for the From header
from = (SipURI) sipFactory.createSipURI(proxyUsername, proxyURI);
} else {
from = sipFactory.createSipURI(((SipURI) request.getFrom().getURI()).getUser(), proxyURI);
}
}
to = sipFactory.createSipURI(((SipURI) request.getTo().getURI()).getUser(), proxyURI);
} catch (Exception exception) {
if(logger.isInfoEnabled()) {
logger.info("Exception: " + exception);
}
}
} else {
if(logger.isInfoEnabled()) {
logger.info("Call to SIP URI. myHostIp: " + myHostIp + " mediaExternalIp: " + mediaExternalIp
+ " toHost: " + toHost + " proxyUri: " + proxyURI);
}
from = sipFactory.createSipURI(((SipURI) request.getFrom().getURI()).getUser(), outboundIntf.getHost()
+ ":" + outboundIntf.getPort());
to = sipFactory.createSipURI(toUser, toHost + ":" + toPort);
callToSipUri = true;
}
if (B2BUAHelper.redirectToB2BUA(request, client, from, to, proxyUsername, proxyPassword, storage,
sipFactory, callToSipUri, patchForNatB2BUASessions)) {
return true;
}
return false;
}
private boolean isWebRTC(final SipServletRequest request) {
String transport = request.getTransport();
String userAgent = request.getHeader(UserAgent.NAME);
//The check for request.getHeader(UserAgentHeader.NAME).equals("sipunit") has been added in order to be able to test this feature with sipunit at the Restcomm testsuite
if (userAgent != null && !userAgent.isEmpty() && userAgent.equalsIgnoreCase("wss-sipunit")) {
return true;
}
if (!request.getInitialTransport().equalsIgnoreCase(transport)) {
transport = request.getInitialTransport();
if ("ws".equalsIgnoreCase(transport) || "wss".equalsIgnoreCase(transport))
return true;
}
try {
if (SdpUtils.isWebRTCSDP(request.getContentType(), request.getRawContent())) {
return true;
}
} catch (SdpParseException e) {}
catch (IOException e) {}
return false;
}
private void proxyThroughMediaServer(final SipServletRequest request, final Client client, final String destNumber) {
String rcml = ""+destNumber+"";
final VoiceInterpreterBuilder builder = new VoiceInterpreterBuilder(supervisor);
builder.setConfiguration(configuration);
builder.setStorage(storage);
builder.setCallManager(self());
builder.setConferenceManager(conferences);
builder.setBridgeManager(bridges);
builder.setSmsService(sms);
builder.setAccount(client.getAccountSid());
builder.setVersion(client.getApiVersion());
final Account account = storage.getAccountsDao().getAccount(client.getAccountSid());
builder.setEmailAddress(account.getEmailAddress());
builder.setRcml(rcml);
builder.setMonitoring(monitoring);
final ActorRef interpreter = builder.build();
final ActorRef call = call();
final SipApplicationSession application = request.getApplicationSession();
application.setAttribute(Call.class.getName(), call);
call.tell(request, self());
interpreter.tell(new StartInterpreter(call), self());
}
private void info(final SipServletRequest request) throws IOException {
final ActorRef self = self();
final SipApplicationSession application = request.getApplicationSession();
// if this response is coming from a client that is in a p2p session with another registered client
// we will just proxy the response
SipSession linkedB2BUASession = B2BUAHelper.getLinkedSession(request);
if (linkedB2BUASession != null) {
if (logger.isInfoEnabled()) {
logger.info(String.format("B2BUA: Got INFO request: \n %s", request));
}
request.getSession().setAttribute(B2BUAHelper.B2BUA_LAST_REQUEST, request);
SipServletRequest clonedInfo = linkedB2BUASession.createRequest("INFO");
linkedB2BUASession.setAttribute(B2BUAHelper.B2BUA_LAST_REQUEST, clonedInfo);
// Issue #307: https://telestax.atlassian.net/browse/RESTCOMM-307
SipURI toInetUri = (SipURI) request.getSession().getAttribute(B2BUAHelper.TO_INET_URI);
SipURI fromInetUri = (SipURI) request.getSession().getAttribute(B2BUAHelper.FROM_INET_URI);
InetAddress infoRURI = null;
try {
infoRURI = InetAddress.getByName(((SipURI) clonedInfo.getRequestURI()).getHost());
} catch (UnknownHostException e) {
}
if (patchForNatB2BUASessions) {
if (toInetUri != null && infoRURI == null) {
if(logger.isInfoEnabled()){
logger.info("Using the real ip address of the sip client " + toInetUri.toString()
+ " as a request uri of the CloneBye request");
}
clonedInfo.setRequestURI(toInetUri);
} else if (toInetUri != null
&& (infoRURI.isSiteLocalAddress() || infoRURI.isAnyLocalAddress() || infoRURI.isLoopbackAddress())) {
if(logger.isInfoEnabled()){
logger.info("Using the real ip address of the sip client " + toInetUri.toString()
+ " as a request uri of the CloneInfo request");
}
clonedInfo.setRequestURI(toInetUri);
} else if (fromInetUri != null
&& (infoRURI.isSiteLocalAddress() || infoRURI.isAnyLocalAddress() || infoRURI.isLoopbackAddress())) {
if(logger.isInfoEnabled()){
logger.info("Using the real ip address of the sip client " + fromInetUri.toString()
+ " as a request uri of the CloneInfo request");
}
clonedInfo.setRequestURI(fromInetUri);
}
}
clonedInfo.send();
} else {
final ActorRef call = (ActorRef) application.getAttribute(Call.class.getName());
call.tell(request, self);
}
}
private void transfer(SipServletRequest request) throws Exception {
//Transferor is the one that initates the transfer
String transferor = ((SipURI)request.getAddressHeader("Contact").getURI()).getUser();
//Transferee is the one that gets transfered
String transferee = ((SipURI)request.getAddressHeader("To").getURI()).getUser();
//Trasnfer target, where the transferee will be transfered
String transferTarget = ((SipURI)request.getAddressHeader("Refer-To").getURI()).getUser();
CallDetailRecord cdr = null;
CallDetailRecordsDao dao = storage.getCallDetailRecordsDao();
SipServletResponse servletResponse = null;
final SipApplicationSession appSession = request.getApplicationSession();
//Initates the transfer
ActorRef transferorActor = (ActorRef) appSession.getAttribute(Call.class.getName());
if (transferorActor == null) {
if (logger.isInfoEnabled()) {
logger.info("Transferor Call Actor is null, cannot proceed with SIP Refer");
}
servletResponse = request.createResponse(SC_NOT_FOUND);
servletResponse.setHeader("Reason", "SIP REFER should be sent in dialog");
servletResponse.setHeader("Event", "refer");
servletResponse.send();
return;
}
final Timeout expires = new Timeout(Duration.create(60, TimeUnit.SECONDS));
Future