adaptation of old eclipse code to Intellij

This commit is contained in:
2024-11-18 12:09:33 +07:00
commit db13021be9
173 changed files with 17794 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip;
import org.pmw.tinylog.Logger;
public abstract class AbstractState {
protected String id;
public AbstractState(String id) {
this.id = id;
}
public void log(AbstractState state) {
StringBuffer buf = new StringBuffer();
buf.append("SM ").append(id).append(" [");
buf.append(JavaUtils.getShortClassName(this.getClass())).append(" -> ");
buf.append(JavaUtils.getShortClassName(state.getClass())).append("] ");
buf.append(new Exception().getStackTrace()[1].getMethodName());
Logger.debug(buf.toString());
}
}

View File

@@ -0,0 +1,29 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip;
public class JavaUtils {
public static String getShortClassName(Class<?> c) {
String name = c.getName();
return name.substring(name.lastIndexOf('.') + 1);
}
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2008, 2009, 2010, 2011 Yohann Martineau
*/
package peers.sip;
public class RFC2617 {
// SCHEMES
public static final String SCHEME_DIGEST = "Digest";
// PARAMETERS
public static final String PARAM_NONCE = "nonce";
public static final String PARAM_OPAQUE = "opaque";
public static final String PARAM_REALM = "realm";
public static final String PARAM_RESPONSE = "response";
public static final String PARAM_URI = "uri";
public static final String PARAM_USERNAME = "username";
public static final String PARAM_QOP = "qop";
public static final String PARAM_CNONCE = "cnonce";
public static final String PARAM_NC = "nc";
public static final String PARAM_ALGORITHM= "algorithm";
// MISCELLANEOUS
public static final char PARAM_SEPARATOR = ',';
public static final char PARAM_VALUE_SEPARATOR = '=';
public static final char PARAM_VALUE_DELIMITER = '"';
public static final char DIGEST_SEPARATOR = ':';
}

169
src/peers/sip/RFC3261.java Normal file
View File

@@ -0,0 +1,169 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip;
public final class RFC3261 {
//SYNTAX ENCODING
//HEADERS
//Methods
public static final String METHOD_INVITE = "INVITE";
public static final String METHOD_ACK = "ACK";
public static final String METHOD_REGISTER = "REGISTER";
public static final String METHOD_BYE = "BYE";
public static final String METHOD_OPTIONS = "OPTIONS";
public static final String METHOD_CANCEL = "CANCEL";
//Classical form
public static final String HDR_ALLOW = "Allow";
public static final String HDR_AUTHORIZATION = "Authorization";
public static final String HDR_CALLID = "Call-ID";
public static final String HDR_CONTACT = "Contact";
public static final String HDR_CONTENT_ENCODING = "Content-Encoding";
public static final String HDR_CONTENT_LENGTH = "Content-Length";
public static final String HDR_CONTENT_TYPE = "Content-Type";
public static final String HDR_CSEQ = "CSeq";
public static final String HDR_FROM = "From";
public static final String HDR_MAX_FORWARDS = "Max-Forwards";
public static final String HDR_RECORD_ROUTE = "Record-Route";
public static final String HDR_PROXY_AUTHENTICATE = "Proxy-Authenticate";
public static final String HDR_PROXY_AUTHORIZATION = "Proxy-Authorization";
public static final String HDR_ROUTE = "Route";
public static final String HDR_SUBJECT = "Subject";
public static final String HDR_SUPPORTED = "Supported";
public static final String HDR_TO = "To";
public static final String HDR_VIA = "Via";
public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate";
//Compact form
public static final char COMPACT_HDR_CALLID = 'i';
public static final char COMPACT_HDR_CONTACT = 'm';
public static final char COMPACT_HDR_CONTENT_ENCODING = 'e';
public static final char COMPACT_HDR_CONTENT_LENGTH = 'l';
public static final char COMPACT_HDR_CONTENT_TYPE = 'c';
public static final char COMPACT_HDR_FROM = 'f';
public static final char COMPACT_HDR_SUBJECT = 's';
public static final char COMPACT_HDR_SUPPORTED = 'k';
public static final char COMPACT_HDR_TO = 't';
public static final char COMPACT_HDR_VIA = 'v';
//Parameters
public static final String PARAM_BRANCH = "branch";
public static final String PARAM_EXPIRES = "expires";
public static final String PARAM_MADDR = "maddr";
public static final String PARAM_RECEIVED = "received";
public static final String PARAM_RPORT = "rport";
public static final String PARAM_SENTBY = "sent-by";
public static final String PARAM_TAG = "tag";
public static final String PARAM_TRANSPORT = "transport";
public static final String PARAM_TTL = "ttl";
public static final String PARAM_SEPARATOR = ";";
public static final String PARAM_ASSIGNMENT = "=";
//Miscellaneous
public static final char FIELD_NAME_SEPARATOR = ':';
public static final String DEFAULT_SIP_VERSION = "SIP/2.0";
public static final String CRLF = "\r\n";
public static final String IPV4_TTL = "1";
public static final char AT = '@';
public static final String LOOSE_ROUTING = "lr";
public static final char LEFT_ANGLE_BRACKET = '<';
public static final char RIGHT_ANGLE_BRACKET = '>';
public static final String HEADER_SEPARATOR = ",";
//STATUS CODES
public static final int CODE_MIN_PROV = 100;
public static final int CODE_MIN_SUCCESS = 200;
public static final int CODE_MIN_REDIR = 300;
public static final int CODE_MAX = 699;
public static final int CODE_100_TRYING = 100;
public static final int CODE_180_RINGING = 180;
public static final int CODE_200_OK = 200;
public static final int CODE_401_UNAUTHORIZED = 401;
public static final int CODE_405_METHOD_NOT_ALLOWED = 405;
public static final int CODE_407_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int CODE_481_CALL_TRANSACTION_DOES_NOT_EXIST = 481;
public static final int CODE_486_BUSYHERE = 486;
public static final int CODE_487_REQUEST_TERMINATED = 487;
public static final int CODE_500_SERVER_INTERNAL_ERROR = 500;
//REASON PHRASES
public static final String REASON_180_RINGING = "Ringing";
public static final String REASON_200_OK = "OK";
public static final String REASON_405_METHOD_NOT_ALLOWED =
"Method Not Allowed";
public static final String REASON_481_CALL_TRANSACTION_DOES_NOT_EXIST =
"Call/Transaction Does Not Exist";
public static final String REASON_486_BUSYHERE = "Busy Here";
public static final String REASON_487_REQUEST_TERMINATED =
"Request Terminated";
public static final String REASON_500_SERVER_INTERNAL_ERROR =
"Server Internal Error";
//TRANSPORT
public static final String TRANSPORT_UDP = "UDP";
public static final String TRANSPORT_TCP = "TCP";
public static final String TRANSPORT_SCTP = "SCTP";
public static final String TRANSPORT_TLS = "TLS";
public static final int TRANSPORT_UDP_USUAL_MAX_SIZE = 1300;
public static final int TRANSPORT_UDP_MAX_SIZE = 65535;
public static final char TRANSPORT_VIA_SEP = '/';
public static final char TRANSPORT_VIA_SEP2 = ' ';
public static final int TRANSPORT_DEFAULT_PORT = 5060;
public static final int TRANSPORT_TLS_PORT = 5061;
public static final char TRANSPORT_PORT_SEP = ':';
//TRANSACTION
//TRANSACTION USER
public static final int DEFAULT_MAXFORWARDS = 70;
public static final String BRANCHID_MAGIC_COOKIE = "z9hG4bK";
public static final String SIP_SCHEME = "sip";
public static final char SCHEME_SEPARATOR = ':';
//TIMERS (in milliseconds)
public static final int TIMER_T1 = 500;
public static final int TIMER_T2 = 4000;
public static final int TIMER_T4 = 5000;
public static final int TIMER_INVITE_CLIENT_TRANSACTION = 32000;
//TRANSACTION USER
//CORE
public static final String CONTENT_TYPE_SDP = "application/sdp";
}

128
src/peers/sip/Utils.java Normal file
View File

@@ -0,0 +1,128 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip;
import java.net.InetAddress;
import peers.sip.core.useragent.UAS;
import peers.sip.syntaxencoding.SipHeaderFieldMultiValue;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transport.SipMessage;
public class Utils {
public static final String PEERSHOME_SYSTEM_PROPERTY = "peers.home";
public static final String DEFAULT_PEERS_HOME = ".";
public final static SipHeaderFieldValue getTopVia(SipMessage sipMessage) {
SipHeaders sipHeaders = sipMessage.getSipHeaders();
SipHeaderFieldName viaName = new SipHeaderFieldName(RFC3261.HDR_VIA);
SipHeaderFieldValue via = sipHeaders.get(viaName);
if (via instanceof SipHeaderFieldMultiValue) {
via = ((SipHeaderFieldMultiValue)via).getValues().get(0);
}
return via;
}
public final static String generateTag() {
return randomString(8);
}
public final static String generateCallID(InetAddress inetAddress) {
//TODO make a hash using current time millis, public ip @, private @, and a random string
StringBuffer buf = new StringBuffer();
buf.append(randomString(8));
buf.append('-');
buf.append(String.valueOf(System.currentTimeMillis()));
buf.append('@');
buf.append(inetAddress.getHostName());
return buf.toString();
}
public final static String generateBranchId() {
StringBuffer buf = new StringBuffer();
buf.append(RFC3261.BRANCHID_MAGIC_COOKIE);
//TODO must be unique across space and time...
buf.append(randomString(9));
return buf.toString();
}
public final static String getMessageCallId(SipMessage sipMessage) {
SipHeaderFieldValue callId = sipMessage.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_CALLID));
return callId.getValue();
}
public final static String randomString(int length) {
String chars = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIFKLMNOPRSTUVWXYZ" +
"0123456789";
StringBuffer buf = new StringBuffer(length);
for (int i = 0; i < length; ++i) {
int pos = (int)Math.round(Math.random() * (chars.length() - 1));
buf.append(chars.charAt(pos));
}
return buf.toString();
}
public final static void copyHeader(SipMessage src, SipMessage dst, String name) {
SipHeaderFieldName sipHeaderFieldName = new SipHeaderFieldName(name);
SipHeaderFieldValue sipHeaderFieldValue = src.getSipHeaders().get(sipHeaderFieldName);
if (sipHeaderFieldValue != null) {
dst.getSipHeaders().add(sipHeaderFieldName, sipHeaderFieldValue);
}
}
public final static String getUserPart(String sipUri) {
int start = sipUri.indexOf(RFC3261.SCHEME_SEPARATOR);
int end = sipUri.indexOf(RFC3261.AT);
return sipUri.substring(start + 1, end);
}
/**
* adds Max-Forwards Supported and Require headers
* @param headers
*/
public final static void addCommonHeaders(SipHeaders headers) {
//Max-Forwards
headers.add(new SipHeaderFieldName(RFC3261.HDR_MAX_FORWARDS),
new SipHeaderFieldValue(
String.valueOf(RFC3261.DEFAULT_MAXFORWARDS)));
//TODO Supported and Require
}
public final static String generateAllowHeader() {
StringBuffer buf = new StringBuffer();
for (String supportedMethod: UAS.SUPPORTED_METHODS) {
buf.append(supportedMethod);
buf.append(", ");
}
int bufLength = buf.length();
buf.delete(bufLength - 2, bufLength);
return buf.toString();
}
}

View File

@@ -0,0 +1,300 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2008-2013 Yohann Martineau
*/
package peers.sip.core.useragent;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC2617;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
public class ChallengeManager implements MessageInterceptor {
public static final String ALGORITHM_MD5 = "MD5";
private String username;
private String password;
private String realm;
private String nonce;
private String opaque;
private String requestUri;
private String digest;
private String profileUri;
private String qop;
private String cnonce;
private static volatile int nonceCount = 1;
private String nonceCountHex;
private Config config;
// FIXME what happens if a challenge is received for a register-refresh
// and another challenge is received in the mean time for an invite?
private int statusCode;
private SipHeaderFieldValue contact;
private InitialRequestManager initialRequestManager;
private MidDialogRequestManager midDialogRequestManager;
private DialogManager dialogManager;
public ChallengeManager(Config config,
InitialRequestManager initialRequestManager,
MidDialogRequestManager midDialogRequestManager,
DialogManager dialogManager) {
this.config = config;
this.initialRequestManager = initialRequestManager;
this.midDialogRequestManager = midDialogRequestManager;
this.dialogManager = dialogManager;
init();
}
private void init() {
username = config.getUserPart();
password = config.getPassword();
profileUri = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR
+ username + RFC3261.AT + config.getDomain();
}
private String md5hash(String message) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance(ALGORITHM_MD5);
} catch (NoSuchAlgorithmException e) {
Logger.error("no such algorithm " + ALGORITHM_MD5, e);
return null;
}
byte[] messageBytes = message.getBytes();
byte[] messageMd5 = messageDigest.digest(messageBytes);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(out);
for (byte b : messageMd5) {
int u_b = (b < 0) ? 256 + b : b;
printStream.printf("%02x", u_b);
}
return out.toString();
}
public void handleChallenge(SipRequest sipRequest,
SipResponse sipResponse) {
init();
statusCode = sipResponse.getStatusCode();
SipHeaders responseHeaders = sipResponse.getSipHeaders();
SipHeaders requestHeaders = sipRequest.getSipHeaders();
contact = requestHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CONTACT));
SipHeaderFieldValue authenticate;
SipHeaderFieldName authenticateHeaderName;
if (statusCode == RFC3261.CODE_401_UNAUTHORIZED) {
authenticateHeaderName = new SipHeaderFieldName(
RFC3261.HDR_WWW_AUTHENTICATE);
} else if (statusCode == RFC3261.CODE_407_PROXY_AUTHENTICATION_REQUIRED) {
authenticateHeaderName = new SipHeaderFieldName(
RFC3261.HDR_PROXY_AUTHENTICATE);
} else {
return;
}
authenticate = responseHeaders.get(authenticateHeaderName);
if (authenticate == null) {
return;
}
if (!authenticate.getValue().startsWith(RFC2617.SCHEME_DIGEST)) {
Logger.info("unsupported challenge scheme in header: "
+ authenticate);
return;
}
String headerValue = authenticate.getValue();
realm = getParameter(RFC2617.PARAM_REALM, headerValue);
nonce = getParameter(RFC2617.PARAM_NONCE, headerValue);
opaque = getParameter(RFC2617.PARAM_OPAQUE, headerValue);
qop = getParameter(RFC2617.PARAM_QOP, headerValue);
if( "auth".equals(qop)) {
nonceCountHex = String.format("%08X", nonceCount++);
}
String method = sipRequest.getMethod();
requestUri = sipRequest.getRequestUri().toString();
cnonce = UUID.randomUUID().toString();
digest = getRequestDigest(method);
// FIXME message should be copied "as is" not created anew from scratch
// and this technique is not clean
String callId = responseHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CALLID)).getValue();
Dialog dialog = dialogManager.getDialog(callId);
if (dialog != null) {
midDialogRequestManager.generateMidDialogRequest(
dialog, RFC3261.METHOD_BYE, this);
} else {
SipHeaderFieldValue from = requestHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_FROM));
String fromTag = from.getParam(new SipHeaderParamName(
RFC3261.PARAM_TAG));
try {
initialRequestManager.createInitialRequest(
requestUri, method, profileUri, callId, fromTag, this);
} catch (SipUriSyntaxException e) {
Logger.error("syntax error", e);
}
}
}
private String getRequestDigest(String method) {
StringBuffer buf = new StringBuffer();
buf.append(username);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(realm);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(password);
String ha1 = md5hash(buf.toString());
buf = new StringBuffer();
buf.append(method);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(requestUri);
String ha2 = md5hash(buf.toString());
buf = new StringBuffer();
buf.append(ha1);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(nonce);
buf.append(RFC2617.DIGEST_SEPARATOR);
if("auth".equals(qop)) {
buf.append(nonceCountHex);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(cnonce);
buf.append(RFC2617.DIGEST_SEPARATOR);
buf.append(qop);
buf.append(RFC2617.DIGEST_SEPARATOR);
}
buf.append(ha2);
return md5hash(buf.toString());
}
private String getParameter(String paramName, String header) {
int paramPos = header.indexOf(paramName);
if (paramPos < 0) {
return null;
}
int paramNameLength = paramName.length();
if (paramPos + paramNameLength + 3 > header.length()) {
Logger.info("Malformed " + RFC3261.HDR_WWW_AUTHENTICATE + " header");
return null;
}
if (header.charAt(paramPos + paramNameLength) !=
RFC2617.PARAM_VALUE_SEPARATOR) {
Logger.info("Malformed " + RFC3261.HDR_WWW_AUTHENTICATE + " header");
return null;
}
if (header.charAt(paramPos + paramNameLength + 1) !=
RFC2617.PARAM_VALUE_DELIMITER) {
Logger.info("Malformed " + RFC3261.HDR_WWW_AUTHENTICATE + " header");
return null;
}
header = header.substring(paramPos + paramNameLength + 2);
int endDelimiter = header.indexOf(RFC2617.PARAM_VALUE_DELIMITER);
if (endDelimiter < 0) {
Logger.info("Malformed " + RFC3261.HDR_WWW_AUTHENTICATE + " header");
return null;
}
return header.substring(0, endDelimiter);
}
/** add xxxAuthorization header */
public void postProcess(SipMessage sipMessage) {
if (realm == null || nonce == null || digest == null) {
return;
}
SipHeaders sipHeaders = sipMessage.getSipHeaders();
String cseq = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).getValue();
String method = cseq.substring(cseq.trim().lastIndexOf(' ') + 1);
digest = getRequestDigest(method);
StringBuffer buf = new StringBuffer();
buf.append(RFC2617.SCHEME_DIGEST).append(" ");
appendParameter(buf, RFC2617.PARAM_USERNAME, username);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_REALM, realm);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_NONCE, nonce);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_URI, requestUri);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_RESPONSE, digest);
if("auth".equals(qop)) {
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_NC, nonceCountHex);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_CNONCE, cnonce);
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_QOP, qop);
}
if (opaque != null) {
buf.append(RFC2617.PARAM_SEPARATOR).append(" ");
appendParameter(buf, RFC2617.PARAM_OPAQUE, opaque);
}
SipHeaderFieldName authorizationName;
if (statusCode == RFC3261.CODE_401_UNAUTHORIZED) {
authorizationName = new SipHeaderFieldName(
RFC3261.HDR_AUTHORIZATION);
} else if (statusCode == RFC3261.CODE_407_PROXY_AUTHENTICATION_REQUIRED) {
authorizationName = new SipHeaderFieldName(
RFC3261.HDR_PROXY_AUTHORIZATION);
} else {
return;
}
sipHeaders.add(authorizationName,
new SipHeaderFieldValue(buf.toString()));
// manage authentication on unregister challenge...
if (contact != null) {
SipHeaderParamName expiresName =
new SipHeaderParamName(RFC3261.PARAM_EXPIRES);
String expiresString = contact.getParam(expiresName);
if (expiresString != null && Integer.parseInt(expiresString) == 0) {
SipHeaderFieldValue requestContact =
sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CONTACT));
requestContact.addParam(expiresName, expiresString);
}
}
}
private void appendParameter(StringBuffer buf, String name, String value) {
buf.append(name);
buf.append(RFC2617.PARAM_VALUE_SEPARATOR);
buf.append(RFC2617.PARAM_VALUE_DELIMITER);
buf.append(value);
buf.append(RFC2617.PARAM_VALUE_DELIMITER);
}
}

View File

@@ -0,0 +1,312 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010, 2011 Yohann Martineau
*/
package peers.sip.core.useragent;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.core.useragent.handlers.ByeHandler;
import peers.sip.core.useragent.handlers.CancelHandler;
import peers.sip.core.useragent.handlers.InviteHandler;
import peers.sip.core.useragent.handlers.OptionsHandler;
import peers.sip.core.useragent.handlers.RegisterHandler;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class InitialRequestManager extends RequestManager
implements ServerTransactionUser {
public InitialRequestManager(UserAgent userAgent,
InviteHandler inviteHandler,
CancelHandler cancelHandler,
ByeHandler byeHandler,
OptionsHandler optionsHandler,
RegisterHandler registerHandler,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager
) {
super(userAgent,
inviteHandler,
cancelHandler,
byeHandler,
optionsHandler,
registerHandler,
dialogManager,
transactionManager,
transportManager);
registerHandler.setInitialRequestManager(this);
}
/**
* gives a new request outside of a dialog
*
* @param requestUri
* @param method
* @return
* @throws SipUriSyntaxException
*/
public SipRequest getGenericRequest(String requestUri, String method,
String profileUri, String callId, String fromTag)
throws SipUriSyntaxException {
//8.1.1
SipRequest request = new SipRequest(method, new SipURI(requestUri));
SipHeaders headers = request.getSipHeaders();
//String hostAddress = utils.getMyAddress().getHostAddress();
//Via
//TODO no Via should be added directly by UAC, Via is normally added by Transaction layer
// StringBuffer viaBuf = new StringBuffer();
// viaBuf.append(RFC3261.DEFAULT_SIP_VERSION);
// // TODO choose real transport
// viaBuf.append("/UDP ");
// viaBuf.append(hostAddress);
// SipHeaderFieldValue via = new SipHeaderFieldValue(viaBuf.toString());
// via.addParam(new SipHeaderParamName(RFC3261.PARAM_BRANCHID),
// utils.generateBranchId());
// headers.add(new SipHeaderFieldName(RFC3261.HDR_VIA), via);
Utils.addCommonHeaders(headers);
//To
NameAddress to = new NameAddress(requestUri);
headers.add(new SipHeaderFieldName(RFC3261.HDR_TO),
new SipHeaderFieldValue(to.toString()));
//From
NameAddress fromNA = new NameAddress(profileUri);
SipHeaderFieldValue from = new SipHeaderFieldValue(fromNA.toString());
String localFromTag;
if (fromTag != null) {
localFromTag = fromTag;
} else {
localFromTag = Utils.generateTag();
}
from.addParam(new SipHeaderParamName(RFC3261.PARAM_TAG), localFromTag);
headers.add(new SipHeaderFieldName(RFC3261.HDR_FROM), from);
//Call-ID
SipHeaderFieldName callIdName =
new SipHeaderFieldName(RFC3261.HDR_CALLID);
String localCallId;
if (callId != null) {
localCallId = callId;
} else {
localCallId = Utils.generateCallID(
userAgent.getConfig().getLocalInetAddress());
}
headers.add(callIdName, new SipHeaderFieldValue(localCallId));
//CSeq
headers.add(new SipHeaderFieldName(RFC3261.HDR_CSEQ),
new SipHeaderFieldValue(userAgent.generateCSeq(method)));
return request;
}
public SipRequest createInitialRequest(String requestUri, String method,
String profileUri) throws SipUriSyntaxException {
return createInitialRequest(requestUri, method, profileUri, null);
}
public SipRequest createInitialRequest(String requestUri, String method,
String profileUri, String callId) throws SipUriSyntaxException {
return createInitialRequest(requestUri, method, profileUri, callId,
null, null);
}
public SipRequest createInitialRequest(String requestUri, String method,
String profileUri, String callId, String fromTag,
MessageInterceptor messageInterceptor)
throws SipUriSyntaxException {
SipRequest sipRequest = getGenericRequest(requestUri, method,
profileUri, callId, fromTag);
// TODO add route header for outbound proxy give it to xxxHandler to create
// clientTransaction
SipURI outboundProxy = userAgent.getOutboundProxy();
if (outboundProxy != null) {
NameAddress outboundProxyNameAddress =
new NameAddress(outboundProxy.toString());
sipRequest.getSipHeaders().add(new SipHeaderFieldName(RFC3261.HDR_ROUTE),
new SipHeaderFieldValue(outboundProxyNameAddress.toString()), 0);
}
ClientTransaction clientTransaction = null;
if (RFC3261.METHOD_INVITE.equals(method)) {
clientTransaction = inviteHandler.preProcessInvite(sipRequest);
} else if (RFC3261.METHOD_REGISTER.equals(method)) {
clientTransaction = registerHandler.preProcessRegister(sipRequest);
}
createInitialRequestEnd(sipRequest, clientTransaction, profileUri,
messageInterceptor, true);
return sipRequest;
}
private void createInitialRequestEnd(SipRequest sipRequest,
ClientTransaction clientTransaction, String profileUri,
MessageInterceptor messageInterceptor, boolean addContact) {
if (clientTransaction == null) {
Logger.error("method not supported");
return;
}
if (addContact) {
addContact(sipRequest, clientTransaction.getContact(), profileUri);
}
if (messageInterceptor != null) {
messageInterceptor.postProcess(sipRequest);
}
// TODO create message receiver on client transport port
clientTransaction.start();
}
public void createCancel(SipRequest inviteRequest,
MidDialogRequestManager midDialogRequestManager, String profileUri) {
SipHeaders inviteHeaders = inviteRequest.getSipHeaders();
SipHeaderFieldValue callId = inviteHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CALLID));
SipRequest sipRequest;
try {
sipRequest = getGenericRequest(
inviteRequest.getRequestUri().toString(), RFC3261.METHOD_CANCEL,
profileUri, callId.getValue(), null);
} catch (SipUriSyntaxException e) {
Logger.error("syntax error", e);
return;
}
ClientTransaction clientTransaction = null;
clientTransaction = cancelHandler.preProcessCancel(sipRequest,
inviteRequest, midDialogRequestManager);
if (clientTransaction != null) {
createInitialRequestEnd(sipRequest, clientTransaction, profileUri,
null, false);
}
}
public void manageInitialRequest(SipRequest sipRequest) {
SipHeaders headers = sipRequest.getSipHeaders();
// TODO authentication
//method inspection
SipResponse sipResponse = null;
if (!UAS.SUPPORTED_METHODS.contains(sipRequest.getMethod())) {
//TODO generate 405 (using 8.2.6 &) with Allow header
//(20.5) and send it
sipResponse = generateResponse(sipRequest, null,
RFC3261.CODE_405_METHOD_NOT_ALLOWED,
RFC3261.REASON_405_METHOD_NOT_ALLOWED);
SipHeaders sipHeaders = sipResponse.getSipHeaders();
sipHeaders.add(new SipHeaderFieldName(RFC3261.HDR_ALLOW),
new SipHeaderFieldValue(Utils.generateAllowHeader()));
}
SipHeaderFieldValue contentType =
headers.get(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE));
if (contentType != null) {
if (!RFC3261.CONTENT_TYPE_SDP.equals(contentType.getValue())) {
//TODO generate 415 with a Accept header listing supported content types
//8.2.3
}
}
//etc.
if (sipResponse != null) {
ServerTransaction serverTransaction =
transactionManager.createServerTransaction(
sipResponse, userAgent.getSipPort(), RFC3261.TRANSPORT_UDP,
this, sipRequest);
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
}
//TODO create server transaction
String method = sipRequest.getMethod();
if (RFC3261.METHOD_INVITE.equals(method)) {
inviteHandler.handleInitialInvite(sipRequest);
} else if (RFC3261.METHOD_CANCEL.equals(method)) {
cancelHandler.handleCancel(sipRequest);
} else if (RFC3261.METHOD_OPTIONS.equals(method)) {
optionsHandler.handleOptions(sipRequest);
}
}
public void addContact(SipRequest sipRequest, String contactEnd,
String profileUri) {
SipHeaders sipHeaders = sipRequest.getSipHeaders();
//Contact
StringBuffer contactBuf = new StringBuffer();
contactBuf.append(RFC3261.SIP_SCHEME);
contactBuf.append(RFC3261.SCHEME_SEPARATOR);
String userPart = Utils.getUserPart(profileUri);
contactBuf.append(userPart);
contactBuf.append(RFC3261.AT);
contactBuf.append(contactEnd);
NameAddress contactNA = new NameAddress(contactBuf.toString());
SipHeaderFieldValue contact =
new SipHeaderFieldValue(contactNA.toString());
sipHeaders.add(new SipHeaderFieldName(RFC3261.HDR_CONTACT),
new SipHeaderFieldValue(contact.toString()));
}
///////////////////////////////////////////////////////////
// ServerTransactionUser methods
///////////////////////////////////////////////////////////
@Override
public void transactionFailure() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,27 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import peers.sip.transport.SipMessage;
public interface MessageInterceptor {
public void postProcess(SipMessage sipMessage);
}

View File

@@ -0,0 +1,251 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Hashtable;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.core.useragent.handlers.ByeHandler;
import peers.sip.core.useragent.handlers.CancelHandler;
import peers.sip.core.useragent.handlers.InviteHandler;
import peers.sip.core.useragent.handlers.OptionsHandler;
import peers.sip.core.useragent.handlers.RegisterHandler;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ClientTransactionUser;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class MidDialogRequestManager extends RequestManager
implements ClientTransactionUser, ServerTransactionUser {
public MidDialogRequestManager(UserAgent userAgent,
InviteHandler inviteHandler,
CancelHandler cancelHandler,
ByeHandler byeHandler,
OptionsHandler optionsHandler,
RegisterHandler registerHandler,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent,
inviteHandler,
cancelHandler,
byeHandler,
optionsHandler,
registerHandler,
dialogManager,
transactionManager,
transportManager);
}
////////////////////////////////////////////////
// methods for UAC
////////////////////////////////////////////////
public void generateMidDialogRequest(Dialog dialog,
String method, MessageInterceptor messageInterceptor) {
SipRequest sipRequest = dialog.buildSubsequentRequest(RFC3261.METHOD_BYE);
if (RFC3261.METHOD_BYE.equals(method)) {
byeHandler.preprocessBye(sipRequest, dialog);
}
//TODO check that subsequent request is supported before client
//transaction creation
if (!RFC3261.METHOD_INVITE.equals(method)) {
ClientTransaction clientTransaction = createNonInviteClientTransaction(
sipRequest, null, byeHandler);
if (messageInterceptor != null) {
messageInterceptor.postProcess(sipRequest);
}
if (clientTransaction != null) {
clientTransaction.start();
}
} else {
//TODO client transaction user is managed by invite handler directly
}
}
public ClientTransaction createNonInviteClientTransaction(
SipRequest sipRequest, String branchId,
ClientTransactionUser clientTransactionUser) {
//8.1.2
SipURI destinationUri = RequestManager.getDestinationUri(sipRequest);
//TODO if header route is present, addrspec = toproute.nameaddress.addrspec
String transport = RFC3261.TRANSPORT_UDP;
Hashtable<String, String> params = destinationUri.getUriParameters();
if (params != null) {
String reqUriTransport = params.get(RFC3261.PARAM_TRANSPORT);
if (reqUriTransport != null) {
transport = reqUriTransport;
}
}
int port = destinationUri.getPort();
if (port == SipURI.DEFAULT_PORT) {
port = RFC3261.TRANSPORT_DEFAULT_PORT;
}
SipURI sipUri = userAgent.getConfig().getOutboundProxy();
if (sipUri == null) {
sipUri = destinationUri;
}
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(sipUri.getHost());
} catch (UnknownHostException e) {
Logger.error("unknown host: " + sipUri.getHost(), e);
return null;
}
ClientTransaction clientTransaction = transactionManager
.createClientTransaction(sipRequest, inetAddress, port, transport,
branchId, clientTransactionUser);
return clientTransaction;
}
////////////////////////////////////////////////
// methods for UAS
////////////////////////////////////////////////
public void manageMidDialogRequest(SipRequest sipRequest, Dialog dialog) {
SipHeaders sipHeaders = sipRequest.getSipHeaders();
SipHeaderFieldValue cseq =
sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CSEQ));
String cseqStr = cseq.getValue();
int pos = cseqStr.indexOf(' ');
if (pos < 0) {
pos = cseqStr.indexOf('\t');
}
int newCseq = Integer.parseInt(cseqStr.substring(0, pos));
int oldCseq = dialog.getRemoteCSeq();
if (oldCseq == Dialog.EMPTY_CSEQ) {
dialog.setRemoteCSeq(newCseq);
} else if (newCseq < oldCseq) {
// out of order
// RFC3261 12.2.2 p77
// TODO test out of order in-dialog-requests
SipResponse sipResponse = generateResponse(sipRequest, dialog,
RFC3261.CODE_500_SERVER_INTERNAL_ERROR,
RFC3261.REASON_500_SERVER_INTERNAL_ERROR);
ServerTransaction serverTransaction =
transactionManager.createServerTransaction(
sipResponse,
userAgent.getSipPort(),
RFC3261.TRANSPORT_UDP,
this, sipRequest);
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
} else {
dialog.setRemoteCSeq(newCseq);
}
String method = sipRequest.getMethod();
if (RFC3261.METHOD_BYE.equals(method)) {
byeHandler.handleBye(sipRequest, dialog);
} else if (RFC3261.METHOD_INVITE.equals(method)) {
inviteHandler.handleReInvite(sipRequest, dialog);
} else if (RFC3261.METHOD_ACK.equals(method)) {
inviteHandler.handleAck(sipRequest, dialog);
} else if (RFC3261.METHOD_OPTIONS.equals(method)) {
optionsHandler.handleOptions(sipRequest);
}
}
///////////////////////////////////////
// ServerTransactionUser methods
///////////////////////////////////////
@Override
public void transactionFailure() {
// TODO Auto-generated method stub
}
///////////////////////////////////////
// ClientTransactionUser methods
///////////////////////////////////////
// callbacks employed for cancel responses (ignored)
@Override
public void transactionTimeout(ClientTransaction clientTransaction) {
// TODO Auto-generated method stub
}
@Override
public void provResponseReceived(SipResponse sipResponse,
Transaction transaction) {
// TODO Auto-generated method stub
}
@Override
public void errResponseReceived(SipResponse sipResponse) {
// TODO Auto-generated method stub
}
@Override
public void successResponseReceived(SipResponse sipResponse,
Transaction transaction) {
// TODO Auto-generated method stub
}
@Override
public void transactionTransportError() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,136 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.core.useragent.handlers.ByeHandler;
import peers.sip.core.useragent.handlers.CancelHandler;
import peers.sip.core.useragent.handlers.InviteHandler;
import peers.sip.core.useragent.handlers.OptionsHandler;
import peers.sip.core.useragent.handlers.RegisterHandler;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public abstract class RequestManager {
public static SipURI getDestinationUri(SipRequest sipRequest
) {
SipHeaders requestHeaders = sipRequest.getSipHeaders();
SipURI destinationUri = null;
SipHeaderFieldValue route = requestHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_ROUTE));
if (route != null) {
try {
destinationUri = new SipURI(
NameAddress.nameAddressToUri(route.toString()));
} catch (SipUriSyntaxException e) {
Logger.error("syntax error", e);
}
}
if (destinationUri == null) {
destinationUri = sipRequest.getRequestUri();
}
return destinationUri;
}
public static SipResponse generateResponse(SipRequest sipRequest,
Dialog dialog, int statusCode, String reasonPhrase) {
//8.2.6.2
SipResponse sipResponse = new SipResponse(statusCode, reasonPhrase);
SipHeaders requestHeaders = sipRequest.getSipHeaders();
SipHeaders responseHeaders = sipResponse.getSipHeaders();
SipHeaderFieldName fromName = new SipHeaderFieldName(RFC3261.HDR_FROM);
responseHeaders.add(fromName, requestHeaders.get(fromName));
SipHeaderFieldName callIdName = new SipHeaderFieldName(RFC3261.HDR_CALLID);
responseHeaders.add(callIdName, requestHeaders.get(callIdName));
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
responseHeaders.add(cseqName, requestHeaders.get(cseqName));
SipHeaderFieldName viaName = new SipHeaderFieldName(RFC3261.HDR_VIA);
responseHeaders.add(viaName, requestHeaders.get(viaName));//TODO check ordering
SipHeaderFieldName toName = new SipHeaderFieldName(RFC3261.HDR_TO);
SipHeaderFieldValue toValue = requestHeaders.get(toName);
SipHeaderParamName toTagParamName = new SipHeaderParamName(RFC3261.PARAM_TAG);
String toTag = toValue.getParam(toTagParamName);
if (toTag == null) {
if (dialog != null) {
toTag = dialog.getLocalTag();
toValue.addParam(toTagParamName, toTag);
}
}
responseHeaders.add(toName, toValue);
return sipResponse;
}
protected InviteHandler inviteHandler;
protected CancelHandler cancelHandler;
protected ByeHandler byeHandler;
protected OptionsHandler optionsHandler;
protected RegisterHandler registerHandler;
protected UserAgent userAgent;
protected TransactionManager transactionManager;
protected TransportManager transportManager;
public RequestManager(UserAgent userAgent,
InviteHandler inviteHandler,
CancelHandler cancelHandler,
ByeHandler byeHandler,
OptionsHandler optionsHandler,
RegisterHandler registerHandler,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
this.userAgent = userAgent;
this.inviteHandler = inviteHandler;
this.cancelHandler = cancelHandler;
this.byeHandler = byeHandler;
this.optionsHandler = optionsHandler;
this.registerHandler = registerHandler;
this.transactionManager = transactionManager;
this.transportManager = transportManager;
}
public InviteHandler getInviteHandler() {
return inviteHandler;
}
public ByeHandler getByeHandler() {
return byeHandler;
}
public RegisterHandler getRegisterHandler() {
return registerHandler;
}
}

View File

@@ -0,0 +1,46 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010, 2011 Yohann Martineau
*/
package peers.sip.core.useragent;
import peers.sip.transport.SipMessage;
public class SipEvent {
public enum EventType {
ERROR, RINGING, INCOMING_CALL, CALLEE_PICKUP;
}
private EventType eventType;
private SipMessage sipMessage;
public SipEvent(EventType type, SipMessage sipMessage) {
this.eventType = type;
this.sipMessage = sipMessage;
}
public SipMessage getSipMessage() {
return sipMessage;
}
public EventType getEventType() {
return eventType;
}
}

View File

@@ -0,0 +1,44 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
public interface SipListener {
public void registering(SipRequest sipRequest);
public void registerSuccessful(SipResponse sipResponse);
public void registerFailed(SipResponse sipResponse);
public void incomingCall(SipRequest sipRequest, SipResponse provResponse);
public void remoteHangup(SipRequest sipRequest);
public void ringing(SipResponse sipResponse);
public void calleePickup(SipResponse sipResponse);
public void error(SipResponse sipResponse);
}

View File

@@ -0,0 +1,227 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.core.useragent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.InviteClientTransaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transactionuser.DialogState;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class UAC {
private InitialRequestManager initialRequestManager;
private MidDialogRequestManager midDialogRequestManager;
private String registerCallID;
private String profileUri;
//FIXME
private UserAgent userAgent;
private TransactionManager transactionManager;
private DialogManager dialogManager;
private List<String> guiClosedCallIds;
/**
* should be instanciated only once, it was a singleton.
*/
public UAC(UserAgent userAgent,
InitialRequestManager initialRequestManager,
MidDialogRequestManager midDialogRequestManager,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
this.userAgent = userAgent;
this.initialRequestManager = initialRequestManager;
this.midDialogRequestManager = midDialogRequestManager;
this.dialogManager = dialogManager;
this.transactionManager = transactionManager;
guiClosedCallIds = Collections.synchronizedList(new ArrayList<String>());
profileUri = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR
+ userAgent.getUserpart() + RFC3261.AT + userAgent.getDomain();
}
/**
* For the moment we consider that only one profile uri is used at a time.
* @throws SipUriSyntaxException
*/
SipRequest register() throws SipUriSyntaxException {
String domain = userAgent.getDomain();
String requestUri = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR
+ domain;
SipListener sipListener = userAgent.getSipListener();
profileUri = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR
+ userAgent.getUserpart() + RFC3261.AT + domain;
registerCallID = Utils.generateCallID(
userAgent.getConfig().getLocalInetAddress());
SipRequest sipRequest = initialRequestManager.createInitialRequest(
requestUri, RFC3261.METHOD_REGISTER, profileUri,
registerCallID);
if (sipListener != null) {
sipListener.registering(sipRequest);
}
return sipRequest;
}
void unregister() throws SipUriSyntaxException {
if (getInitialRequestManager().getRegisterHandler().isRegistered()) {
String requestUri = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR
+ userAgent.getDomain();
MessageInterceptor messageInterceptor = new MessageInterceptor() {
@Override
public void postProcess(SipMessage sipMessage) {
initialRequestManager.registerHandler.unregister();
SipHeaders sipHeaders = sipMessage.getSipHeaders();
SipHeaderFieldValue contact = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CONTACT));
contact.addParam(new SipHeaderParamName(RFC3261.PARAM_EXPIRES),
"0");
}
};
// for any reason, asterisk requires a new Call-ID to unregister
registerCallID = Utils.generateCallID(
userAgent.getConfig().getLocalInetAddress());
initialRequestManager.createInitialRequest(requestUri,
RFC3261.METHOD_REGISTER, profileUri, registerCallID, null,
messageInterceptor);
}
}
SipRequest invite(String requestUri, String callId)
throws SipUriSyntaxException {
return initialRequestManager.createInitialRequest(requestUri,
RFC3261.METHOD_INVITE, profileUri, callId);
}
private SipRequest getInviteWithAuth(String callId) {
List<ClientTransaction> clientTransactions =
transactionManager.getClientTransactionsFromCallId(callId,
RFC3261.METHOD_INVITE);
SipRequest sipRequestNoAuth = null;
for (ClientTransaction clientTransaction: clientTransactions) {
InviteClientTransaction inviteClientTransaction =
(InviteClientTransaction)clientTransaction;
SipRequest sipRequest = inviteClientTransaction.getRequest();
SipHeaders sipHeaders = sipRequest.getSipHeaders();
SipHeaderFieldName authorization = new SipHeaderFieldName(
RFC3261.HDR_AUTHORIZATION);
SipHeaderFieldValue value = sipHeaders.get(authorization);
if (value == null) {
SipHeaderFieldName proxyAuthorization = new SipHeaderFieldName(
RFC3261.HDR_PROXY_AUTHORIZATION);
value = sipHeaders.get(proxyAuthorization);
}
if (value != null) {
return sipRequest;
}
sipRequestNoAuth = sipRequest;
}
return sipRequestNoAuth;
}
void terminate(SipRequest sipRequest) {
String callId = Utils.getMessageCallId(sipRequest);
if (!guiClosedCallIds.contains(callId)) {
guiClosedCallIds.add(callId);
}
Dialog dialog = dialogManager.getDialog(callId);
SipRequest inviteWithAuth = getInviteWithAuth(callId);
if (dialog != null) {
SipRequest originatingRequest;
if (inviteWithAuth != null) {
originatingRequest = inviteWithAuth;
} else {
originatingRequest = sipRequest;
}
ClientTransaction clientTransaction =
transactionManager.getClientTransaction(originatingRequest);
if (clientTransaction != null) {
synchronized (clientTransaction) {
DialogState dialogState = dialog.getState();
if (dialog.EARLY.equals(dialogState)) {
initialRequestManager.createCancel(inviteWithAuth,
midDialogRequestManager, profileUri);
} else if (dialog.CONFIRMED.equals(dialogState)) {
// clientTransaction not yet removed
midDialogRequestManager.generateMidDialogRequest(
dialog, RFC3261.METHOD_BYE, null);
guiClosedCallIds.remove(callId);
}
}
} else {
// clientTransaction Terminated and removed
Logger.debug("clientTransaction null");
midDialogRequestManager.generateMidDialogRequest(
dialog, RFC3261.METHOD_BYE, null);
guiClosedCallIds.remove(callId);
}
} else {
InviteClientTransaction inviteClientTransaction =
(InviteClientTransaction)transactionManager
.getClientTransaction(inviteWithAuth);
if (inviteClientTransaction == null) {
Logger.error("cannot find invite client transaction" +
" for call " + callId);
} else {
SipResponse sipResponse =
inviteClientTransaction.getLastResponse();
if (sipResponse != null) {
int statusCode = sipResponse.getStatusCode();
if (statusCode < RFC3261.CODE_200_OK) {
initialRequestManager.createCancel(inviteWithAuth,
midDialogRequestManager, profileUri);
}
}
}
}
userAgent.getMediaManager().stopSession();
}
public InitialRequestManager getInitialRequestManager() {
return initialRequestManager;
}
public List<String> getGuiClosedCallIds() {
return guiClosedCallIds;
}
}

View File

@@ -0,0 +1,133 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import java.net.SocketException;
import java.util.ArrayList;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.SipServerTransportUser;
import peers.sip.transport.TransportManager;
public class UAS implements SipServerTransportUser {
public final static ArrayList<String> SUPPORTED_METHODS;
static {
SUPPORTED_METHODS = new ArrayList<String>();
SUPPORTED_METHODS.add(RFC3261.METHOD_INVITE);
SUPPORTED_METHODS.add(RFC3261.METHOD_ACK);
SUPPORTED_METHODS.add(RFC3261.METHOD_CANCEL);
SUPPORTED_METHODS.add(RFC3261.METHOD_OPTIONS);
SUPPORTED_METHODS.add(RFC3261.METHOD_BYE);
};
private InitialRequestManager initialRequestManager;
private MidDialogRequestManager midDialogRequestManager;
private DialogManager dialogManager;
/**
* should be instanciated only once, it was a singleton.
*/
public UAS(UserAgent userAgent,
InitialRequestManager initialRequestManager,
MidDialogRequestManager midDialogRequestManager,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) throws SocketException {
this.initialRequestManager = initialRequestManager;
this.midDialogRequestManager = midDialogRequestManager;
this.dialogManager = dialogManager;
transportManager.setSipServerTransportUser(this);
transportManager.createServerTransport(
RFC3261.TRANSPORT_UDP, userAgent.getConfig().getSipPort());
}
public void messageReceived(SipMessage sipMessage) {
if (sipMessage instanceof SipRequest) {
requestReceived((SipRequest) sipMessage);
} else if (sipMessage instanceof SipResponse) {
responseReceived((SipResponse) sipMessage);
} else {
throw new RuntimeException("unknown message type");
}
}
private void responseReceived(SipResponse sipResponse) {
}
private void requestReceived(SipRequest sipRequest) {
//TODO 8.2
//TODO JTA to make request processing atomic
SipHeaders headers = sipRequest.getSipHeaders();
//TODO find whether the request is within an existing dialog or not
SipHeaderFieldValue to =
headers.get(new SipHeaderFieldName(RFC3261.HDR_TO));
String toTag = to.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
if (toTag != null) {
Dialog dialog = dialogManager.getDialog(sipRequest);
if (dialog != null) {
//this is a mid-dialog request
midDialogRequestManager.manageMidDialogRequest(sipRequest, dialog);
//TODO continue processing
} else {
//TODO reject the request with a 481 Call/Transaction Does Not Exist
}
} else {
initialRequestManager.manageInitialRequest(sipRequest);
}
}
void acceptCall(SipRequest sipRequest, Dialog dialog) {
initialRequestManager.getInviteHandler().acceptCall(sipRequest,
dialog);
}
void rejectCall(SipRequest sipRequest) {
initialRequestManager.getInviteHandler().rejectCall(sipRequest);
}
public InitialRequestManager getInitialRequestManager() {
return initialRequestManager;
}
public MidDialogRequestManager getMidDialogRequestManager() {
return midDialogRequestManager;
}
}

View File

@@ -0,0 +1,382 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent;
import java.io.File;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.XmlConfig;
import peers.media.AbstractSoundManager;
import peers.media.Echo;
import peers.media.MediaManager;
import peers.media.MediaMode;
import peers.sdp.SDPManager;
import peers.sip.Utils;
import peers.sip.core.useragent.handlers.ByeHandler;
import peers.sip.core.useragent.handlers.CancelHandler;
import peers.sip.core.useragent.handlers.InviteHandler;
import peers.sip.core.useragent.handlers.OptionsHandler;
import peers.sip.core.useragent.handlers.RegisterHandler;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
@SuppressWarnings("unused")
public class UserAgent {
public final static String CONFIG_FILE = "conf" + File.separator + "peers.xml";
public final static int RTP_DEFAULT_PORT = 8000;
private String peersHome;
private Config config;
private List<String> peers;
//private List<Dialog> dialogs;
//TODO factorize echo and captureRtpSender
private Echo echo;
private UAC uac;
private UAS uas;
private ChallengeManager challengeManager;
private DialogManager dialogManager;
private TransactionManager transactionManager;
private TransportManager transportManager;
private int cseqCounter;
private SipListener sipListener;
private SDPManager sdpManager;
private AbstractSoundManager soundManager;
private MediaManager mediaManager;
public UserAgent(SipListener sipListener, String peersHome,
AbstractSoundManager soundManager)
throws SocketException {
this(sipListener, null, peersHome, soundManager);
}
public UserAgent(SipListener sipListener, Config config,
AbstractSoundManager soundManager)
throws SocketException {
this(sipListener, config, null, soundManager);
}
private UserAgent(SipListener sipListener, Config config, String peersHome,
AbstractSoundManager soundManager)
throws SocketException {
this.sipListener = sipListener;
if (peersHome == null) {
peersHome = Utils.DEFAULT_PEERS_HOME;
}
this.peersHome = peersHome;
if (config == null) {
config = new XmlConfig(this.peersHome + File.separator
+ CONFIG_FILE);
}
this.config = config;
cseqCounter = 1;
StringBuffer buf = new StringBuffer();
buf.append("starting user agent [");
buf.append("myAddress: ");
buf.append(config.getLocalInetAddress().getHostAddress()).append(", ");
buf.append("sipPort: ");
buf.append(config.getSipPort()).append(", ");
buf.append("userpart: ");
buf.append(config.getUserPart()).append(", ");
buf.append("domain: ");
buf.append(config.getDomain()).append("]");
Logger.info(buf.toString());
//transaction user
dialogManager = new DialogManager();
//transaction
transactionManager = new TransactionManager();
//transport
transportManager = new TransportManager(transactionManager,
config);
transactionManager.setTransportManager(transportManager);
//core
InviteHandler inviteHandler = new InviteHandler(this,
dialogManager,
transactionManager,
transportManager);
CancelHandler cancelHandler = new CancelHandler(this,
dialogManager,
transactionManager,
transportManager);
ByeHandler byeHandler = new ByeHandler(this,
dialogManager,
transactionManager,
transportManager);
OptionsHandler optionsHandler = new OptionsHandler(this,
transactionManager,
transportManager);
RegisterHandler registerHandler = new RegisterHandler(this,
transactionManager,
transportManager);
InitialRequestManager initialRequestManager =
new InitialRequestManager(
this,
inviteHandler,
cancelHandler,
byeHandler,
optionsHandler,
registerHandler,
dialogManager,
transactionManager,
transportManager);
MidDialogRequestManager midDialogRequestManager =
new MidDialogRequestManager(
this,
inviteHandler,
cancelHandler,
byeHandler,
optionsHandler,
registerHandler,
dialogManager,
transactionManager,
transportManager);
uas = new UAS(this,
initialRequestManager,
midDialogRequestManager,
dialogManager,
transactionManager,
transportManager);
uac = new UAC(this,
initialRequestManager,
midDialogRequestManager,
dialogManager,
transactionManager,
transportManager);
challengeManager = new ChallengeManager(config,
initialRequestManager,
midDialogRequestManager,
dialogManager);
registerHandler.setChallengeManager(challengeManager);
inviteHandler.setChallengeManager(challengeManager);
byeHandler.setChallengeManager(challengeManager);
peers = new ArrayList<String>();
//dialogs = new ArrayList<Dialog>();
sdpManager = new SDPManager(this);
inviteHandler.setSdpManager(sdpManager);
optionsHandler.setSdpManager(sdpManager);
this.soundManager = soundManager;
mediaManager = new MediaManager(this);
}
// client methods
public void close() {
transportManager.closeTransports();
config.setPublicInetAddress(null);
}
public SipRequest register() throws SipUriSyntaxException {
return uac.register();
}
public void unregister() throws SipUriSyntaxException {
uac.unregister();
}
public SipRequest invite(String requestUri, String callId)
throws SipUriSyntaxException {
return uac.invite(requestUri, callId);
}
public void terminate(SipRequest sipRequest) {
uac.terminate(sipRequest);
}
public void acceptCall(SipRequest sipRequest, Dialog dialog) {
uas.acceptCall(sipRequest, dialog);
}
public void rejectCall(SipRequest sipRequest) {
uas.rejectCall(sipRequest);
}
/**
* Gives the sipMessage if sipMessage is a SipRequest or
* the SipRequest corresponding to the SipResponse
* if sipMessage is a SipResponse
* @param sipMessage
* @return null if sipMessage is neither a SipRequest neither a SipResponse
*/
public SipRequest getSipRequest(SipMessage sipMessage) {
if (sipMessage instanceof SipRequest) {
return (SipRequest) sipMessage;
} else if (sipMessage instanceof SipResponse) {
SipResponse sipResponse = (SipResponse) sipMessage;
Transaction transaction = (Transaction)transactionManager
.getClientTransaction(sipResponse);
if (transaction == null) {
transaction = (Transaction)transactionManager
.getServerTransaction(sipResponse);
}
if (transaction == null) {
return null;
}
return transaction.getRequest();
} else {
return null;
}
}
// public List<Dialog> getDialogs() {
// return dialogs;
// }
public List<String> getPeers() {
return peers;
}
// public Dialog getDialog(String peer) {
// for (Dialog dialog : dialogs) {
// String remoteUri = dialog.getRemoteUri();
// if (remoteUri != null) {
// if (remoteUri.contains(peer)) {
// return dialog;
// }
// }
// }
// return null;
// }
public String generateCSeq(String method) {
StringBuffer buf = new StringBuffer();
buf.append(cseqCounter++);
buf.append(' ');
buf.append(method);
return buf.toString();
}
public boolean isRegistered() {
return uac.getInitialRequestManager().getRegisterHandler()
.isRegistered();
}
public UAS getUas() {
return uas;
}
public UAC getUac() {
return uac;
}
public DialogManager getDialogManager() {
return dialogManager;
}
public int getSipPort() {
return transportManager.getSipPort();
}
public int getRtpPort() {
return config.getRtpPort();
}
public String getDomain() {
return config.getDomain();
}
public String getUserpart() {
return config.getUserPart();
}
public MediaMode getMediaMode() {
return config.getMediaMode();
}
public boolean isMediaDebug() {
return config.isMediaDebug();
}
public SipURI getOutboundProxy() {
return config.getOutboundProxy();
}
public Echo getEcho() {
return echo;
}
public void setEcho(Echo echo) {
this.echo = echo;
}
public SipListener getSipListener() {
return sipListener;
}
public AbstractSoundManager getSoundManager() {
return soundManager;
}
public MediaManager getMediaManager() {
return mediaManager;
}
public Config getConfig() {
return config;
}
public String getPeersHome() {
return peersHome;
}
public TransportManager getTransportManager() {
return transportManager;
}
}

View File

@@ -0,0 +1,183 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.core.useragent.RequestManager;
import peers.sip.core.useragent.SipListener;
import peers.sip.core.useragent.UserAgent;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ClientTransactionUser;
import peers.sip.transaction.NonInviteClientTransaction;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class ByeHandler extends DialogMethodHandler
implements ServerTransactionUser, ClientTransactionUser {
public ByeHandler(UserAgent userAgent, DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, dialogManager, transactionManager, transportManager);
}
////////////////////////////////////////////////
// methods for UAC
////////////////////////////////////////////////
public void preprocessBye(SipRequest sipRequest, Dialog dialog) {
// 15.1.1
String addrSpec = sipRequest.getRequestUri().toString();
userAgent.getPeers().remove(addrSpec);
challengeManager.postProcess(sipRequest);
}
////////////////////////////////////////////////
// methods for UAS
////////////////////////////////////////////////
public void handleBye(SipRequest sipRequest, Dialog dialog) {
dialog.receivedOrSentBye();
//String remoteUri = dialog.getRemoteUri();
String addrSpec = sipRequest.getRequestUri().toString();
userAgent.getPeers().remove(addrSpec);
dialogManager.removeDialog(dialog.getId());
Logger.debug("removed dialog " + dialog.getId());
userAgent.getMediaManager().stopSession();
SipResponse sipResponse =
RequestManager.generateResponse(
sipRequest,
dialog,
RFC3261.CODE_200_OK,
RFC3261.REASON_200_OK);
// TODO determine port and transport for server transaction>transport
// from initial invite
// FIXME determine port and transport for server transaction>transport
ServerTransaction serverTransaction = transactionManager
.createServerTransaction(
sipResponse,
userAgent.getSipPort(),
RFC3261.TRANSPORT_UDP,
this,
sipRequest);
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
dialogManager.removeDialog(dialog.getId());
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.remoteHangup(sipRequest);
}
// setChanged();
// notifyObservers(sipRequest);
}
///////////////////////////////////////
//ServerTransactionUser methods
///////////////////////////////////////
public void transactionFailure() {
// TODO Auto-generated method stub
}
///////////////////////////////////////
//ClientTransactionUser methods
///////////////////////////////////////
@Override
public void transactionTimeout(ClientTransaction clientTransaction) {
// TODO Auto-generated method stub
}
@Override
public void provResponseReceived(SipResponse sipResponse,
Transaction transaction) {
// TODO Auto-generated method stub
}
@Override
public void errResponseReceived(SipResponse sipResponse) {
int statusCode = sipResponse.getStatusCode();
if (statusCode == RFC3261.CODE_401_UNAUTHORIZED
|| statusCode == RFC3261.CODE_407_PROXY_AUTHENTICATION_REQUIRED
&& !challenged) {
NonInviteClientTransaction nonInviteClientTransaction =
(NonInviteClientTransaction)
transactionManager.getClientTransaction(sipResponse);
SipRequest sipRequest = nonInviteClientTransaction.getRequest();
String password = userAgent.getConfig().getPassword();
if (password != null && !"".equals(password.trim())) {
challengeManager.handleChallenge(sipRequest,
sipResponse);
}
challenged = true;
} else {
challenged = false;
}
}
@Override
public void successResponseReceived(SipResponse sipResponse,
Transaction transaction) {
Dialog dialog = dialogManager.getDialog(sipResponse);
if (dialog == null) {
return;
}
dialog.receivedOrSentBye();
dialogManager.removeDialog(dialog.getId());
Logger.debug("removed dialog " + dialog.getId());
}
@Override
public void transactionTransportError() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,172 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.core.useragent.MidDialogRequestManager;
import peers.sip.core.useragent.SipListener;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.InviteClientTransaction;
import peers.sip.transaction.InviteServerTransaction;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class CancelHandler extends DialogMethodHandler
implements ServerTransactionUser {
public CancelHandler(UserAgent userAgent, DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, dialogManager, transactionManager, transportManager);
}
//////////////////////////////////////////////////////////
// UAS methods
//////////////////////////////////////////////////////////
public void handleCancel(SipRequest sipRequest) {
SipHeaderFieldValue topVia = Utils.getTopVia(sipRequest);
String branchId = topVia.getParam(new SipHeaderParamName(
RFC3261.PARAM_BRANCH));
InviteServerTransaction inviteServerTransaction =
(InviteServerTransaction)transactionManager
.getServerTransaction(branchId,RFC3261.METHOD_INVITE);
SipResponse cancelResponse;
if (inviteServerTransaction == null) {
//TODO generate CANCEL 481 Call Leg/Transaction Does Not Exist
cancelResponse = buildGenericResponse(sipRequest,
RFC3261.CODE_481_CALL_TRANSACTION_DOES_NOT_EXIST,
RFC3261.REASON_481_CALL_TRANSACTION_DOES_NOT_EXIST);
} else {
cancelResponse = buildGenericResponse(sipRequest,
RFC3261.CODE_200_OK, RFC3261.REASON_200_OK);
}
ServerTransaction cancelServerTransaction = transactionManager
.createServerTransaction(cancelResponse,
userAgent.getSipPort(),
RFC3261.TRANSPORT_UDP, this, sipRequest);
cancelServerTransaction.start();
cancelServerTransaction.receivedRequest(sipRequest);
cancelServerTransaction.sendReponse(cancelResponse);
if (cancelResponse.getStatusCode() != RFC3261.CODE_200_OK) {
return;
}
SipResponse lastResponse = inviteServerTransaction.getLastResponse();
if (lastResponse != null &&
lastResponse.getStatusCode() >= RFC3261.CODE_200_OK) {
return;
}
SipResponse inviteResponse = buildGenericResponse(
inviteServerTransaction.getRequest(),
RFC3261.CODE_487_REQUEST_TERMINATED,
RFC3261.REASON_487_REQUEST_TERMINATED);
inviteServerTransaction.sendReponse(inviteResponse);
Dialog dialog = dialogManager.getDialog(lastResponse);
dialog.receivedOrSent300To699();
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.remoteHangup(sipRequest);
}
}
//////////////////////////////////////////////////////////
// UAC methods
//////////////////////////////////////////////////////////
public ClientTransaction preProcessCancel(SipRequest cancelGenericRequest,
SipRequest inviteRequest,
MidDialogRequestManager midDialogRequestManager) {
//TODO
//p. 54 §9.1
SipHeaders cancelHeaders = cancelGenericRequest.getSipHeaders();
SipHeaders inviteHeaders = inviteRequest.getSipHeaders();
//cseq
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
SipHeaderFieldValue cancelCseq = cancelHeaders.get(cseqName);
SipHeaderFieldValue inviteCseq = inviteHeaders.get(cseqName);
cancelCseq.setValue(inviteCseq.getValue().replace(RFC3261.METHOD_INVITE,
RFC3261.METHOD_CANCEL));
//from
SipHeaderFieldName fromName = new SipHeaderFieldName(RFC3261.HDR_FROM);
SipHeaderFieldValue cancelFrom = cancelHeaders.get(fromName);
SipHeaderFieldValue inviteFrom = inviteHeaders.get(fromName);
cancelFrom.setValue(inviteFrom.getValue());
SipHeaderParamName tagParam = new SipHeaderParamName(RFC3261.PARAM_TAG);
cancelFrom.removeParam(tagParam);
cancelFrom.addParam(tagParam, inviteFrom.getParam(tagParam));
//top-via
// cancelHeaders.add(new SipHeaderFieldName(RFC3261.HDR_VIA),
// Utils.getInstance().getTopVia(inviteRequest));
SipHeaderFieldValue topVia = Utils.getTopVia(inviteRequest);
String branchId = topVia.getParam(new SipHeaderParamName(RFC3261.PARAM_BRANCH));
//route
SipHeaderFieldName routeName = new SipHeaderFieldName(RFC3261.HDR_ROUTE);
SipHeaderFieldValue inviteRoute = inviteHeaders.get(routeName);
if (inviteRoute != null) {
cancelHeaders.add(routeName, inviteRoute);
}
InviteClientTransaction inviteClientTransaction =
(InviteClientTransaction)transactionManager.getClientTransaction(
inviteRequest);
if (inviteClientTransaction != null) {
SipResponse lastResponse = inviteClientTransaction.getLastResponse();
if (lastResponse != null &&
lastResponse.getStatusCode() >= RFC3261.CODE_200_OK) {
return null;
}
} else {
Logger.error("cannot retrieve invite client transaction for"
+ " request " + inviteRequest);
}
return midDialogRequestManager.createNonInviteClientTransaction(
cancelGenericRequest, branchId, midDialogRequestManager);
}
public void transactionFailure() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,283 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldMultiValue;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public abstract class DialogMethodHandler extends MethodHandler {
protected DialogManager dialogManager;
public DialogMethodHandler(UserAgent userAgent,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, transactionManager, transportManager);
this.dialogManager = dialogManager;
}
protected Dialog buildDialogForUas(SipResponse sipResponse,
SipRequest sipRequest) {
//12.1.1
//prepare response
SipHeaders reqHeaders = sipRequest.getSipHeaders();
SipHeaders respHeaders = sipResponse.getSipHeaders();
//copy record-route
SipHeaderFieldName recordRouteName =
new SipHeaderFieldName(RFC3261.HDR_RECORD_ROUTE);
SipHeaderFieldValue reqRecRoute = reqHeaders.get(recordRouteName);
if (reqRecRoute != null) {
respHeaders.add(recordRouteName, reqRecRoute);
}
//FIXME Contact header should probably added in response here.
SipHeaderFieldName contactName = new SipHeaderFieldName(RFC3261.HDR_CONTACT);
Dialog dialog = dialogManager.createDialog(sipResponse);
//build dialog state
//route set
SipHeaderFieldValue recordRoute =
respHeaders.get(new SipHeaderFieldName(RFC3261.HDR_RECORD_ROUTE));
ArrayList<String> routeSet = new ArrayList<String>();
if (recordRoute != null) {
if (recordRoute instanceof SipHeaderFieldMultiValue) {
SipHeaderFieldMultiValue multiRecordRoute =
(SipHeaderFieldMultiValue) recordRoute;
for (SipHeaderFieldValue routeValue : multiRecordRoute.getValues()) {
routeSet.add(routeValue.getValue());
}
} else {
routeSet.add(recordRoute.getValue());
}
}
dialog.setRouteSet(routeSet);
//remote target
SipHeaderFieldValue reqContact = reqHeaders.get(contactName);
String remoteTarget = reqContact.getValue();
if (remoteTarget.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
remoteTarget = NameAddress.nameAddressToUri(remoteTarget);
}
dialog.setRemoteTarget(remoteTarget);
//remote cseq
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
SipHeaderFieldValue cseq = reqHeaders.get(cseqName);
String remoteCseq = cseq.getValue().substring(0, cseq.getValue().indexOf(' '));
dialog.setRemoteCSeq(Integer.parseInt(remoteCseq));
//callid
SipHeaderFieldName callidName = new SipHeaderFieldName(RFC3261.HDR_CALLID);
SipHeaderFieldValue callid = reqHeaders.get(callidName);
dialog.setCallId(callid.getValue());
//local tag
SipHeaderFieldName toName = new SipHeaderFieldName(RFC3261.HDR_TO);
SipHeaderFieldValue to = respHeaders.get(toName);
SipHeaderParamName tagName = new SipHeaderParamName(RFC3261.PARAM_TAG);
String toTag = to.getParam(tagName);
dialog.setLocalTag(toTag);
//remote tag
SipHeaderFieldName fromName = new SipHeaderFieldName(RFC3261.HDR_FROM);
SipHeaderFieldValue from = reqHeaders.get(fromName);
String fromTag = from.getParam(tagName);
dialog.setRemoteTag(fromTag);
//remote uri
String remoteUri = from.getValue();
if (remoteUri.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
remoteUri = NameAddress.nameAddressToUri(remoteUri);
}
dialog.setRemoteUri(remoteUri);
//local uri
String localUri = to.getValue();
if (localUri.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
localUri = NameAddress.nameAddressToUri(localUri);
}
dialog.setLocalUri(localUri);
return dialog;
}
//TODO throw an exception if dialog elements are lacking when dialog is built from 200
protected Dialog buildOrUpdateDialogForUac(SipResponse sipResponse,
Transaction transaction) {
SipHeaders headers = sipResponse.getSipHeaders();
Dialog dialog = dialogManager.getDialog(sipResponse);
if (dialog == null) {
dialog = dialogManager.createDialog(sipResponse);
}
//12.1.2
//TODO if request uri contains sips scheme or if sent over tls => dialog.setSecure(true)
//route set
ArrayList<String> routeSet = computeRouteSet(headers);
if (routeSet != null) {
dialog.setRouteSet(routeSet);
}
//remote target
SipHeaderFieldValue contact = headers.get(new SipHeaderFieldName(RFC3261.HDR_CONTACT));
Logger.debug("Contact: " + contact);
if (contact != null) {
String remoteTarget = NameAddress.nameAddressToUri(contact.toString());
dialog.setRemoteTarget(remoteTarget);
}
SipHeaders requestSipHeaders = transaction.getRequest().getSipHeaders();
//local cseq
String requestCSeq = requestSipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).toString();
requestCSeq = requestCSeq.substring(0, requestCSeq.indexOf(' '));
dialog.setLocalCSeq(Integer.parseInt(requestCSeq));
//callID
//already done in createDialog()
// String requestCallID = requestSipHeaders.get(
// new SipHeaderFieldName(RFC3261.HDR_CALLID)).toString();
// dialog.setCallId(requestCallID);
//local tag
//already done in createDialog()
// SipHeaderFieldValue requestFrom = requestSipHeaders.get(
// new SipHeaderFieldName(RFC3261.HDR_FROM));
// String requestFromTag =
// requestFrom.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
// dialog.setLocalTag(requestFromTag);
//remote tag
//already done in createDialog()
// dialog.setRemoteTag(toTag);
//remote uri
SipHeaderFieldValue to = headers.get(new SipHeaderFieldName(RFC3261.HDR_TO));
if (to != null) {
String remoteUri = to.getValue();
if (remoteUri.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
remoteUri = NameAddress.nameAddressToUri(remoteUri);
}
dialog.setRemoteUri(remoteUri);
}
//local uri
SipHeaderFieldValue requestFrom = requestSipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_FROM));
String localUri = requestFrom.getValue();
if (localUri.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
localUri = NameAddress.nameAddressToUri(localUri);
}
dialog.setLocalUri(localUri);
return dialog;
}
protected ArrayList<String> computeRouteSet(SipHeaders headers) {
SipHeaderFieldValue recordRoute =
headers.get(new SipHeaderFieldName(RFC3261.HDR_RECORD_ROUTE));
ArrayList<String> routeSet = new ArrayList<String>();
if (recordRoute != null) {
if (recordRoute instanceof SipHeaderFieldMultiValue) {
List<SipHeaderFieldValue> values =
((SipHeaderFieldMultiValue)recordRoute).getValues();
for (SipHeaderFieldValue value : values) {
//reverse order
routeSet.add(0, value.toString());
}
} else {
routeSet.add(recordRoute.toString());
}
}
return routeSet;
}
//TODO see if AckHandler is usable
class AckTimerTask extends TimerTask {
private String toUri;
public AckTimerTask(String toUri) {
super();
this.toUri = toUri;
}
@Override
public void run() {
ArrayList<Dialog> purgedDialogs = new ArrayList<Dialog>();
Collection<Dialog> dialogs = dialogManager.getDialogCollection();
for (Dialog dialog : dialogs) {
String remoteUri = dialog.getRemoteUri();
if (remoteUri.equals(toUri) &&
!dialog.CONFIRMED.equals(dialog.getState())) {
dialog.receivedOrSentBye();
purgedDialogs.add(dialog);
}
}
for (Dialog dialog : purgedDialogs) {
dialogs.remove(dialog);
}
}
}
}

View File

@@ -0,0 +1,693 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Timer;
import org.pmw.tinylog.Logger;
import peers.media.MediaManager;
import peers.sdp.Codec;
import peers.sdp.MediaDestination;
import peers.sdp.NoCodecException;
import peers.sdp.SessionDescription;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.core.useragent.RequestManager;
import peers.sip.core.useragent.SipListener;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ClientTransactionUser;
import peers.sip.transaction.InviteClientTransaction;
import peers.sip.transaction.InviteServerTransaction;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.MessageSender;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class InviteHandler extends DialogMethodHandler
implements ServerTransactionUser, ClientTransactionUser {
public static final int TIMEOUT = 100;
private MediaDestination mediaDestination;
private Timer ackTimer;
private boolean initialIncomingInvite;
public InviteHandler(UserAgent userAgent,
DialogManager dialogManager,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, dialogManager, transactionManager, transportManager);
ackTimer = new Timer(getClass().getSimpleName() + " Ack "
+ Timer.class.getSimpleName());
}
//////////////////////////////////////////////////////////
// UAS methods
//////////////////////////////////////////////////////////
public void handleInitialInvite(SipRequest sipRequest) {
initialIncomingInvite = true;
//generate 180 Ringing
SipResponse sipResponse = buildGenericResponse(sipRequest,
RFC3261.CODE_180_RINGING, RFC3261.REASON_180_RINGING);
Dialog dialog = buildDialogForUas(sipResponse, sipRequest);
//here dialog is already stored in dialogs in DialogManager
InviteServerTransaction inviteServerTransaction = (InviteServerTransaction)
transactionManager.createServerTransaction(sipResponse,
userAgent.getSipPort(), RFC3261.TRANSPORT_UDP, this,
sipRequest);
inviteServerTransaction.start();
inviteServerTransaction.receivedRequest(sipRequest);
//TODO send 180 more than once
inviteServerTransaction.sendReponse(sipResponse);
dialog.receivedOrSent1xx();
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.incomingCall(sipRequest, sipResponse);
}
List<String> peers = userAgent.getPeers();
String responseTo = sipRequest.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_FROM)).getValue();
if (!peers.contains(responseTo)) {
peers.add(responseTo);
}
}
public void handleReInvite(SipRequest sipRequest, Dialog dialog) {
Logger.debug("handleReInvite");
initialIncomingInvite = false;
SipHeaders sipHeaders = sipRequest.getSipHeaders();
// 12.2.2 update dialog
SipHeaderFieldValue contact =
sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CONTACT));
if (contact != null) {
String contactStr = contact.getValue();
if (contactStr.indexOf(RFC3261.LEFT_ANGLE_BRACKET) > -1) {
contactStr = NameAddress.nameAddressToUri(contactStr);
}
dialog.setRemoteTarget(contactStr);
}
// update session
sendSuccessfulResponse(sipRequest, dialog);
}
private DatagramSocket getDatagramSocket() {
DatagramSocket datagramSocket = userAgent.getMediaManager()
.getDatagramSocket();
if (datagramSocket == null) { // initial invite success response
// AccessController.doPrivileged added for plugin compatibility
datagramSocket = AccessController.doPrivileged(
new PrivilegedAction<DatagramSocket>() {
@Override
public DatagramSocket run() {
DatagramSocket datagramSocket = null;
int rtpPort = userAgent.getConfig().getRtpPort();
try {
if (rtpPort == 0) {
int localPort = -1;
while (localPort % 2 != 0) {
datagramSocket = new DatagramSocket();
localPort = datagramSocket.getLocalPort();
if (localPort % 2 != 0) {
datagramSocket.close();
}
}
} else {
datagramSocket = new DatagramSocket(rtpPort);
}
} catch (SocketException e) {
Logger.error("cannot create datagram socket ", e);
}
return datagramSocket;
}
}
);
Logger.debug("new rtp DatagramSocket " + datagramSocket.hashCode());
try {
datagramSocket.setSoTimeout(TIMEOUT);
} catch (SocketException e) {
Logger.error("cannot set timeout on datagram socket ", e);
}
userAgent.getMediaManager().setDatagramSocket(datagramSocket);
}
return datagramSocket;
}
@SuppressWarnings("unlikely-arg-type")
private synchronized void sendSuccessfulResponse(SipRequest sipRequest, Dialog dialog) {
SipHeaders reqHeaders = sipRequest.getSipHeaders();
SipHeaderFieldValue contentType =
reqHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE));
if (RFC3261.CONTENT_TYPE_SDP.equals(contentType)) {
//TODO
// String sdpResponse;
// try {
// sdpResponse = sdpManager.handleOffer(
// new String(sipRequest.getBody()));
// } catch (NoCodecException e) {
// sdpResponse = sdpManager.generateErrorResponse();
// }
} else {
// TODO manage empty bodies and non-application/sdp content type
}
//TODO if mode autoanswer just send 200 without asking any question
SipResponse sipResponse =
RequestManager.generateResponse(
sipRequest,
dialog,
RFC3261.CODE_200_OK,
RFC3261.REASON_200_OK);
// TODO 13.3 dialog invite-specific processing
// TODO timer if there is an Expires header in INVITE
// TODO 3xx
// TODO 486 or 600
byte[] offerBytes = sipRequest.getBody();
SessionDescription answer;
try {
DatagramSocket datagramSocket = getDatagramSocket();
if (offerBytes != null && contentType != null &&
RFC3261.CONTENT_TYPE_SDP.equals(contentType.getValue())) {
// create response in 200
try {
SessionDescription offer = sdpManager.parse(offerBytes);
answer = sdpManager.createSessionDescription(offer,
datagramSocket.getLocalPort());
mediaDestination = sdpManager.getMediaDestination(offer);
} catch (NoCodecException e) {
answer = sdpManager.createSessionDescription(null,
datagramSocket.getLocalPort());
}
} else {
// create offer in 200 (never tested...)
answer = sdpManager.createSessionDescription(null,
datagramSocket.getLocalPort());
}
sipResponse.setBody(answer.toString().getBytes());
} catch (IOException e) {
Logger.error(e.getMessage(), e);
}
SipHeaders respHeaders = sipResponse.getSipHeaders();
respHeaders.add(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE),
new SipHeaderFieldValue(RFC3261.CONTENT_TYPE_SDP));
ArrayList<String> routeSet = dialog.getRouteSet();
if (routeSet != null) {
SipHeaderFieldName recordRoute = new SipHeaderFieldName(RFC3261.HDR_RECORD_ROUTE);
for (String route : routeSet) {
respHeaders.add(recordRoute, new SipHeaderFieldValue(route));
}
}
// TODO determine port and transport for server transaction>transport
// from initial invite
// FIXME determine port and transport for server transaction>transport
ServerTransaction serverTransaction = transactionManager
.getServerTransaction(sipRequest);
if (serverTransaction == null) {
// in re-INVITE case, no serverTransaction has been created
serverTransaction = (InviteServerTransaction)
transactionManager.createServerTransaction(sipResponse,
userAgent.getSipPort(), RFC3261.TRANSPORT_UDP, this,
sipRequest);
}
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
// TODO manage retransmission of the response (send to the transport)
// until ACK arrives, if no ACK is received within 64*T1, confirm dialog
// and terminate it with a BYE
// logger.getInstance().debug("before dialog.receivedOrSent2xx();");
// logger.getInstance().debug("dialog state: " + dialog.getState());
}
public void acceptCall(SipRequest sipRequest, Dialog dialog) {
sendSuccessfulResponse(sipRequest, dialog);
dialog.receivedOrSent2xx();
// logger.getInstance().debug("dialog state: " + dialog.getState());
// logger.getInstance().debug("after dialog.receivedOrSent2xx();");
// setChanged();
// notifyObservers(sipRequest);
}
public void rejectCall(SipRequest sipRequest) {
//TODO generate 486, etc.
SipHeaders reqHeaders = sipRequest.getSipHeaders();
SipHeaderFieldValue callId = reqHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CALLID));
Dialog dialog = dialogManager.getDialog(callId.getValue());
//TODO manage auto reject Do not disturb (DND)
SipResponse sipResponse =
RequestManager.generateResponse(
sipRequest,
dialog,
RFC3261.CODE_486_BUSYHERE,
RFC3261.REASON_486_BUSYHERE);
// TODO determine port and transport for server transaction>transport
// from initial invite
// FIXME determine port and transport for server transaction>transport
ServerTransaction serverTransaction = transactionManager
.getServerTransaction(sipRequest);
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
dialog.receivedOrSent300To699();
userAgent.getMediaManager().setDatagramSocket(null);
// setChanged();
// notifyObservers(sipRequest);
}
//////////////////////////////////////////////////////////
// UAC methods
//////////////////////////////////////////////////////////
public ClientTransaction preProcessInvite(SipRequest sipRequest)
throws SipUriSyntaxException {
//8.1.2
SipHeaders requestHeaders = sipRequest.getSipHeaders();
SipURI destinationUri = RequestManager.getDestinationUri(sipRequest);
//TODO if header route is present, addrspec = toproute.nameaddress.addrspec
String transport = RFC3261.TRANSPORT_UDP;
Hashtable<String, String> params = destinationUri.getUriParameters();
if (params != null) {
String reqUriTransport = params.get(RFC3261.PARAM_TRANSPORT);
if (reqUriTransport != null) {
transport = reqUriTransport;
}
}
int port = destinationUri.getPort();
if (port == SipURI.DEFAULT_PORT) {
port = RFC3261.TRANSPORT_DEFAULT_PORT;
}
SipURI sipUri = userAgent.getConfig().getOutboundProxy();
if (sipUri == null) {
sipUri = destinationUri;
}
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(sipUri.getHost());
} catch (UnknownHostException e) {
throw new SipUriSyntaxException("unknown host: "
+ sipUri.getHost(), e);
}
ClientTransaction clientTransaction = transactionManager
.createClientTransaction(sipRequest, inetAddress,
port, transport, null, this);
DatagramSocket datagramSocket;
synchronized (this) {
datagramSocket = getDatagramSocket();
}
try {
SessionDescription sessionDescription =
sdpManager.createSessionDescription(null,
datagramSocket.getLocalPort());
sipRequest.setBody(sessionDescription.toString().getBytes());
} catch (IOException e) {
Logger.error(e.getMessage(), e);
}
requestHeaders.add(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE),
new SipHeaderFieldValue(RFC3261.CONTENT_TYPE_SDP));
return clientTransaction;
}
public void preProcessReInvite(SipRequest sipRequest) {
//TODO
}
//////////////////////////////////////////////////////////
// ClientTransactionUser methods
//////////////////////////////////////////////////////////
public void errResponseReceived(final SipResponse sipResponse) {
Dialog dialog = dialogManager.getDialog(sipResponse);
if (dialog != null) {
dialog.receivedOrSent300To699();
dialogManager.removeDialog(dialog.getId());
}
int statusCode = sipResponse.getStatusCode();
if (statusCode == RFC3261.CODE_401_UNAUTHORIZED
|| statusCode == RFC3261.CODE_407_PROXY_AUTHENTICATION_REQUIRED
&& !challenged) {
InviteClientTransaction inviteClientTransaction =
(InviteClientTransaction)
transactionManager.getClientTransaction(sipResponse);
SipRequest sipRequest = inviteClientTransaction.getRequest();
String password = userAgent.getConfig().getPassword();
if (password != null && !"".equals(password.trim())) {
challengeManager.handleChallenge(sipRequest,
sipResponse);
}
challenged = true;
return;
} else {
challenged = false;
}
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.error(sipResponse);
}
List<String> guiClosedCallIds = userAgent.getUac().getGuiClosedCallIds();
String callId = Utils.getMessageCallId(sipResponse);
if (guiClosedCallIds.contains(callId)) {
guiClosedCallIds.remove(callId);
}
userAgent.getMediaManager().setDatagramSocket(null);
}
public void provResponseReceived(SipResponse sipResponse, Transaction transaction) {
// dialog may have already been created if a previous 1xx has
// already been received
Dialog dialog = dialogManager.getDialog(sipResponse);
boolean isFirstProvRespWithToTag = false;
if (dialog == null) {
SipHeaderFieldValue to = sipResponse.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_TO));
String toTag = to.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
if (toTag != null) {
dialog = dialogManager.createDialog(sipResponse);
isFirstProvRespWithToTag = true;
} else {
//TODO maybe stop retransmissions
}
}
if (dialog != null) {
buildOrUpdateDialogForUac(sipResponse, transaction);
}
//
// if (dialog == null && sipResponse.getStatusCode() != RFC3261.CODE_100_TRYING) {
// Logger.debug("dialog not found for prov response");
// isFirstProvRespWithToTag = true;
// SipHeaderFieldValue to = sipResponse.getSipHeaders()
// .get(new SipHeaderFieldName(RFC3261.HDR_TO));
// String toTag = to.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
// if (toTag != null) {
// dialog = buildOrUpdateDialogForUac(sipResponse, transaction);
// }
// }
//TODO this notification is probably useless because dialog state modification
// thereafter always notify dialog observers
if (isFirstProvRespWithToTag) {
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.ringing(sipResponse);
}
dialog.receivedOrSent1xx();
}
List<String> guiClosedCallIds = userAgent.getUac().getGuiClosedCallIds();
String callId = Utils.getMessageCallId(sipResponse);
if (guiClosedCallIds.contains(callId)) {
SipRequest sipRequest = transaction.getRequest();
Logger.debug("cancel after prov response: sipRequest " + sipRequest
+ ", sipResponse " + sipResponse);
userAgent.terminate(sipRequest);
}
}
public void successResponseReceived(SipResponse sipResponse, Transaction transaction) {
SipHeaders responseHeaders = sipResponse.getSipHeaders();
String cseq = responseHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).getValue();
String method = cseq.substring(cseq.trim().lastIndexOf(' ') + 1);
if (!RFC3261.METHOD_INVITE.equals(method)) {
return;
}
challenged = false;
//13.2.2.4
List<String> peers = userAgent.getPeers();
String responseTo = responseHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_TO)).getValue();
if (!peers.contains(responseTo)) {
peers.add(responseTo);
//timer used to purge dialogs which are not confirmed
//after a given time
ackTimer.schedule(new AckTimerTask(responseTo),
64 * RFC3261.TIMER_T1);
}
Dialog dialog = dialogManager.getDialog(sipResponse);
if (dialog != null) {
//dialog already created with a 180 for example
dialog.setRouteSet(computeRouteSet(sipResponse.getSipHeaders()));
}
dialog = buildOrUpdateDialogForUac(sipResponse, transaction);
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.calleePickup(sipResponse);
}
//added for media
SessionDescription sessionDescription =
sdpManager.parse(sipResponse.getBody());
try {
mediaDestination = sdpManager.getMediaDestination(sessionDescription);
} catch (NoCodecException e) {
Logger.error(e.getMessage(), e);
}
String remoteAddress = mediaDestination.getDestination();
int remotePort = mediaDestination.getPort();
Codec codec = mediaDestination.getCodec();
String localAddress = userAgent.getConfig()
.getLocalInetAddress().getHostAddress();
userAgent.getMediaManager().successResponseReceived(localAddress,
remoteAddress, remotePort, codec);
//switch to confirmed state
dialog.receivedOrSent2xx();
//generate ack
//p. 82 §3
SipRequest ack = dialog.buildSubsequentRequest(RFC3261.METHOD_ACK);
//update CSeq
SipHeaders ackHeaders = ack.getSipHeaders();
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
SipHeaderFieldValue ackCseq = ackHeaders.get(cseqName);
SipRequest request = transaction.getRequest();
SipHeaders requestHeaders = request.getSipHeaders();
SipHeaderFieldValue requestCseq = requestHeaders.get(cseqName);
ackCseq.setValue(requestCseq.toString().replace(RFC3261.METHOD_INVITE, RFC3261.METHOD_ACK));
//add Via with only the branchid parameter
SipHeaderFieldValue via = new SipHeaderFieldValue("");
SipHeaderParamName branchIdName = new SipHeaderParamName(RFC3261.PARAM_BRANCH);
via.addParam(branchIdName, Utils.generateBranchId());
ackHeaders.add(new SipHeaderFieldName(RFC3261.HDR_VIA), via, 0);
//TODO authentication headers
if (request.getBody() == null && sipResponse.getBody() != null) {
//TODO add a real SDP answer
ack.setBody(sipResponse.getBody());
}
//TODO check if sdp is acceptable
SipURI destinationUri = RequestManager.getDestinationUri(ack);
challengeManager.postProcess(ack);
//TODO if header route is present, addrspec = toproute.nameaddress.addrspec
String transport = RFC3261.TRANSPORT_UDP;
Hashtable<String, String> params = destinationUri.getUriParameters();
if (params != null) {
String reqUriTransport = params.get(RFC3261.PARAM_TRANSPORT);
if (reqUriTransport != null) {
transport = reqUriTransport;
}
}
int port = destinationUri.getPort();
if (port == SipURI.DEFAULT_PORT) {
port = RFC3261.TRANSPORT_DEFAULT_PORT;
}
SipURI sipUri = userAgent.getConfig().getOutboundProxy();
if (sipUri == null) {
sipUri = destinationUri;
}
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(sipUri.getHost());
} catch (UnknownHostException e) {
Logger.error("unknown host: " + sipUri.getHost(), e);
return;
}
try {
MessageSender sender = transportManager.createClientTransport(
ack, inetAddress, port, transport);
sender.sendMessage(ack);
} catch (IOException e) {
Logger.error("input/output error", e);
}
List<String> guiClosedCallIds = userAgent.getUac().getGuiClosedCallIds();
String callId = Utils.getMessageCallId(sipResponse);
if (guiClosedCallIds.contains(callId)) {
userAgent.terminate(request);
}
}
public void handleAck(SipRequest ack, Dialog dialog) {
// TODO determine if ACK is ACK of an initial INVITE or a re-INVITE
// in first case, captureRtpSender and incomingRtpReader must be
// created, in the second case, they must be updated.
Logger.debug("handleAck");
if (mediaDestination == null) {
SipHeaders reqHeaders = ack.getSipHeaders();
SipHeaderFieldValue contentType =
reqHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE));
byte[] offerBytes = ack.getBody();
if (offerBytes != null && contentType != null &&
RFC3261.CONTENT_TYPE_SDP.equals(contentType.getValue())) {
// create response in 200
try {
SessionDescription answer = sdpManager.parse(offerBytes);
mediaDestination = sdpManager.getMediaDestination(answer);
} catch (NoCodecException e) {
Logger.error(e.getMessage(), e);
return;
}
}
}
String destAddress = mediaDestination.getDestination();
int destPort = mediaDestination.getPort();
Codec codec = mediaDestination.getCodec();
MediaManager mediaManager = userAgent.getMediaManager();
if (initialIncomingInvite) {
mediaManager.handleAck(destAddress, destPort, codec);
} else {
mediaManager.updateRemote(destAddress, destPort, codec);
}
}
public void transactionTimeout(ClientTransaction clientTransaction) {
// TODO Auto-generated method stub
}
public void transactionTransportError() {
// TODO Auto-generated method stub
}
//////////////////////////////////////////////////////////
// ServerTransactionUser methods
//////////////////////////////////////////////////////////
public void transactionFailure() {
// TODO manage transaction failure (ACK was not received)
}
}

View File

@@ -0,0 +1,86 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import peers.sdp.SDPManager;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.core.useragent.ChallengeManager;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transaction.TransactionManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public abstract class MethodHandler {
protected UserAgent userAgent;
protected TransactionManager transactionManager;
protected TransportManager transportManager;
protected ChallengeManager challengeManager;
protected SDPManager sdpManager;
protected boolean challenged;
public MethodHandler(UserAgent userAgent,
TransactionManager transactionManager,
TransportManager transportManager) {
this.userAgent = userAgent;
this.transactionManager = transactionManager;
this.transportManager = transportManager;
challenged = false;
}
protected SipResponse buildGenericResponse(SipRequest sipRequest,
int statusCode, String reasonPhrase) {
//8.2.6
SipResponse sipResponse = new SipResponse(statusCode, reasonPhrase);
SipHeaders respHeaders = sipResponse.getSipHeaders();
SipHeaders reqHeaders = sipRequest.getSipHeaders();
SipHeaderFieldName fromName = new SipHeaderFieldName(RFC3261.HDR_FROM);
respHeaders.add(fromName, reqHeaders.get(fromName));
SipHeaderFieldName callIdName = new SipHeaderFieldName(RFC3261.HDR_CALLID);
respHeaders.add(callIdName, reqHeaders.get(callIdName));
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
respHeaders.add(cseqName, reqHeaders.get(cseqName));
SipHeaderFieldName viaName = new SipHeaderFieldName(RFC3261.HDR_VIA);
respHeaders.add(viaName, reqHeaders.get(viaName));
SipHeaderFieldName toName = new SipHeaderFieldName(RFC3261.HDR_TO);
String to = reqHeaders.get(toName).getValue();
SipHeaderFieldValue toValue = new SipHeaderFieldValue(to);
toValue.addParam(new SipHeaderParamName(RFC3261.PARAM_TAG),
Utils.randomString(10));// TODO 19.3
respHeaders.add(toName, toValue);
return sipResponse;
}
public void setChallengeManager(ChallengeManager challengeManager) {
this.challengeManager = challengeManager;
}
public void setSdpManager(SDPManager sdpManager) {
this.sdpManager = sdpManager;
}
}

View File

@@ -0,0 +1,83 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010, 2012 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import java.io.IOException;
import java.util.Random;
import org.pmw.tinylog.Logger;
import peers.sdp.SessionDescription;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.ServerTransactionUser;
import peers.sip.transaction.TransactionManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class OptionsHandler extends MethodHandler
implements ServerTransactionUser {
public static final int MAX_PORTS = 65536;
public OptionsHandler(UserAgent userAgent,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, transactionManager, transportManager);
}
public void handleOptions(SipRequest sipRequest) {
SipResponse sipResponse = buildGenericResponse(sipRequest,
RFC3261.CODE_200_OK, RFC3261.REASON_200_OK);
int localPort = new Random().nextInt(MAX_PORTS);
try {
SessionDescription sessionDescription =
sdpManager.createSessionDescription(null, localPort);
sipResponse.setBody(sessionDescription.toString().getBytes());
} catch (IOException e) {
Logger.error(e.getMessage(), e);
}
SipHeaders sipHeaders = sipResponse.getSipHeaders();
sipHeaders.add(new SipHeaderFieldName(RFC3261.HDR_CONTENT_TYPE),
new SipHeaderFieldValue(RFC3261.CONTENT_TYPE_SDP));
sipHeaders.add(new SipHeaderFieldName(RFC3261.HDR_ALLOW),
new SipHeaderFieldValue(Utils.generateAllowHeader()));
ServerTransaction serverTransaction =
transactionManager.createServerTransaction(
sipResponse, userAgent.getSipPort(), RFC3261.TRANSPORT_UDP,
this, sipRequest);
serverTransaction.start();
serverTransaction.receivedRequest(sipRequest);
serverTransaction.sendReponse(sipResponse);
}
@Override
public void transactionFailure() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,293 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.core.useragent.handlers;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Hashtable;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
import peers.sip.core.useragent.InitialRequestManager;
import peers.sip.core.useragent.RequestManager;
import peers.sip.core.useragent.SipListener;
import peers.sip.core.useragent.UserAgent;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ClientTransactionUser;
import peers.sip.transaction.NonInviteClientTransaction;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class RegisterHandler extends MethodHandler
implements ClientTransactionUser {
public static final int REFRESH_MARGIN = 10; // seconds
private InitialRequestManager initialRequestManager;
private Timer timer;
private String requestUriStr;
private String profileUriStr;
private String callIDStr;
//FIXME should be on a profile based context
private boolean unregisterInvoked;
private boolean registered;
public RegisterHandler(UserAgent userAgent,
TransactionManager transactionManager,
TransportManager transportManager) {
super(userAgent, transactionManager, transportManager);
}
//TODO factorize common code here and in invitehandler
public synchronized ClientTransaction preProcessRegister(SipRequest sipRequest)
throws SipUriSyntaxException {
registered = false;
unregisterInvoked = false;
SipHeaders sipHeaders = sipRequest.getSipHeaders();
SipURI destinationUri = RequestManager.getDestinationUri(sipRequest);
int port = destinationUri.getPort();
if (port == SipURI.DEFAULT_PORT) {
port = RFC3261.TRANSPORT_DEFAULT_PORT;
}
//TODO if header route is present, addrspec = toproute.nameaddress.addrspec
String transport = RFC3261.TRANSPORT_UDP;
Hashtable<String, String> params = destinationUri.getUriParameters();
if (params != null) {
String reqUriTransport = params.get(RFC3261.PARAM_TRANSPORT);
if (reqUriTransport != null) {
transport = reqUriTransport;
}
}
SipURI sipUri = userAgent.getConfig().getOutboundProxy();
if (sipUri == null) {
sipUri = destinationUri;
}
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(sipUri.getHost());
} catch (UnknownHostException e) {
throw new SipUriSyntaxException("unknown host: "
+ sipUri.getHost(), e);
}
ClientTransaction clientTransaction = transactionManager
.createClientTransaction(sipRequest, inetAddress, port,
transport, null, this);
//TODO 10.2
SipHeaderFieldValue to = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_TO));
SipHeaderFieldValue from = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_FROM));
String fromValue = from.getValue();
to.setValue(fromValue);
requestUriStr = destinationUri.toString();
profileUriStr = NameAddress.nameAddressToUri(fromValue);
callIDStr = sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_CALLID))
.toString();
return clientTransaction;
}
public void unregister() {
timer.cancel();
unregisterInvoked = true;
challenged = false;
}
//////////////////////////////////////////////////////////
// ClientTransactionUser methods
//////////////////////////////////////////////////////////
public void errResponseReceived(SipResponse sipResponse) {
String password = userAgent.getConfig().getPassword();
if (password != null && !"".equals(password.trim())) {
int statusCode = sipResponse.getStatusCode();
if (statusCode == RFC3261.CODE_401_UNAUTHORIZED
|| statusCode ==
RFC3261.CODE_407_PROXY_AUTHENTICATION_REQUIRED) {
if (challenged) {
notifyListener(sipResponse);
} else {
challenged = true;
NonInviteClientTransaction nonInviteClientTransaction =
(NonInviteClientTransaction)
transactionManager.getClientTransaction(sipResponse);
SipRequest sipRequest =
nonInviteClientTransaction.getRequest();
challengeManager.handleChallenge(sipRequest, sipResponse);
}
} else { // not 401 nor 407
SipHeaders sipHeaders = sipResponse.getSipHeaders();
SipHeaderFieldName viaName = new SipHeaderFieldName(
RFC3261.HDR_VIA);
SipHeaderFieldValue via = sipHeaders.get(viaName);
SipHeaderParamName receivedName = new SipHeaderParamName(
RFC3261.PARAM_RECEIVED);
String viaValue = via.getValue();
int pos = viaValue.indexOf(" ");
if (pos > -1) {
viaValue = viaValue.substring(pos + 1);
pos = viaValue.indexOf(RFC3261.TRANSPORT_PORT_SEP);
if (pos > -1) {
viaValue = viaValue.substring(0, pos);
} else {
pos = viaValue.indexOf(RFC3261.PARAM_SEPARATOR);
if (pos > -1) {
viaValue = viaValue.substring(0, pos);
}
}
}
String received = via.getParam(receivedName);
if (received != null && !"".equals(received.trim())) {
if (viaValue.equals(received)) {
notifyListener(sipResponse);
} else { // received != via ip address
try {
InetAddress receivedInetAddress =
InetAddress.getByName(received);
Config config = userAgent.getConfig();
config.setPublicInetAddress(receivedInetAddress);
userAgent.register();
} catch (UnknownHostException e) {
notifyListener(sipResponse);
Logger.error(e.getMessage(), e);
} catch (SipUriSyntaxException e) {
notifyListener(sipResponse);
Logger.error(e.getMessage(), e);
}
}
} else { // received not provided
notifyListener(sipResponse);
}
}
} else { // no password configured
notifyListener(sipResponse);
}
}
private void notifyListener(SipResponse sipResponse) {
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.registerFailed(sipResponse);
}
challenged = false;
}
public void provResponseReceived(SipResponse sipResponse,
Transaction transaction) {
//meaningless
}
public synchronized void successResponseReceived(SipResponse sipResponse,
Transaction transaction) {
// 1. retrieve request corresponding to response
// 2. if request was not an unregister, extract contact and expires,
// and start register refresh timer
// 3. notify sip listener of register success event.
SipRequest sipRequest = transaction.getRequest();
SipHeaderFieldName contactName = new SipHeaderFieldName(
RFC3261.HDR_CONTACT);
SipHeaderFieldValue requestContact = sipRequest.getSipHeaders()
.get(contactName);
SipHeaderParamName expiresParam = new SipHeaderParamName(
RFC3261.PARAM_EXPIRES);
String expires = requestContact.getParam(expiresParam);
challenged = false;
if (!"0".equals(expires)) {
// each contact contains an expires parameter giving the expiration
// in seconds. Thus the binding must be refreshed before it expires.
SipHeaders sipHeaders = sipResponse.getSipHeaders();
SipHeaderFieldValue responseContact = sipHeaders.get(contactName);
if (responseContact == null) {
return;
}
expires = responseContact.getParam(expiresParam);
// patch mobicents simple application
registered = true;
int delay = -1;
if (expires == null || "".equals(expires.trim())) {
delay = 3600;
}
if (!unregisterInvoked) {
if (delay == -1) {
delay = Integer.parseInt(expires) - REFRESH_MARGIN;
}
timer = new Timer(getClass().getSimpleName()
+ " refresh timer");
timer.schedule(new RefreshTimerTask(), delay * 1000);
}
}
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.registerSuccessful(sipResponse);
}
}
public void transactionTimeout(ClientTransaction clientTransaction) {
SipListener sipListener = userAgent.getSipListener();
if (sipListener != null) {
sipListener.registerFailed(null);
}
}
public void transactionTransportError() {
//TODO alert user
}
public boolean isRegistered() {
return registered;
}
//////////////////////////////////////////////////////////
// TimerTask
//////////////////////////////////////////////////////////
class RefreshTimerTask extends TimerTask {
@Override
public void run() {
try {
initialRequestManager.createInitialRequest(requestUriStr,
RFC3261.METHOD_REGISTER, profileUriStr, callIDStr);
} catch (SipUriSyntaxException e) {
Logger.error("syntax error", e);
}
}
}
public void setInitialRequestManager(InitialRequestManager initialRequestManager) {
this.initialRequestManager = initialRequestManager;
}
}

View File

@@ -0,0 +1,66 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import peers.sip.RFC3261;
public class NameAddress {
public static String nameAddressToUri(String nameAddress) {
int leftPos = nameAddress.indexOf(RFC3261.LEFT_ANGLE_BRACKET);
int rightPos = nameAddress.indexOf(RFC3261.RIGHT_ANGLE_BRACKET);
if (leftPos < 0 || rightPos < 0) {
return nameAddress;
}
return nameAddress.substring(leftPos + 1, rightPos);
}
protected String addrSpec;
protected String displayName;
public NameAddress(String addrSpec) {
super();
this.addrSpec = addrSpec;
}
public NameAddress(String addrSpec, String displayName) {
super();
this.addrSpec = addrSpec;
this.displayName = displayName;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
if (displayName != null) {
buf.append(displayName);
buf.append(' ');
}
buf.append(RFC3261.LEFT_ANGLE_BRACKET);
buf.append(addrSpec);
buf.append(RFC3261.RIGHT_ANGLE_BRACKET);
return buf.toString();
}
public String getAddrSpec() {
return addrSpec;
}
}

View File

@@ -0,0 +1,29 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class RunnableSipParser implements Runnable {
public void run() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,54 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class SipHeader {
private SipHeaderFieldName name;
private SipHeaderFieldValue value;
SipHeader(SipHeaderFieldName name, SipHeaderFieldValue value) {
super();
this.name = name;
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SipHeader) {
SipHeader objHdr = (SipHeader) obj;
return name.equals(objHdr.name);
}
return false;
}
public SipHeaderFieldName getName() {
return name;
}
public SipHeaderFieldValue getValue() {
return value;
}
public void setValue(SipHeaderFieldValue value) {
this.value = value;
}
}

View File

@@ -0,0 +1,49 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.util.List;
public class SipHeaderFieldMultiValue extends SipHeaderFieldValue {
private List<SipHeaderFieldValue> values;
private static String toString(List<SipHeaderFieldValue> list) {
if (list == null) {
return null;
}
String arrToString = list.toString();
return arrToString.substring(1, arrToString.length() - 1);
}
public SipHeaderFieldMultiValue(List<SipHeaderFieldValue> values) {
super(toString(values));
this.values = values;
}
public List<SipHeaderFieldValue> getValues() {
return values;
}
@Override
public String toString() {
return toString(values);
}
}

View File

@@ -0,0 +1,63 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class SipHeaderFieldName {
private final static SipHeadersTable SIP_HEADER_TABLE =
new SipHeadersTable();
private String name;
public SipHeaderFieldName(String name) {
super();
if (name.length() == 1) {
this.name = SIP_HEADER_TABLE.getLongForm(name.charAt(0));
} else {
this.name = name;
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
String objName = ((SipHeaderFieldName)obj).getName();
if (name.equalsIgnoreCase(objName)) {
return true;
}
return false;
}
@Override
public int hashCode() {
return name.hashCode();
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,103 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.util.HashMap;
import peers.sip.RFC3261;
public class SipHeaderFieldValue {
private String value;
private HashMap<SipHeaderParamName, String> params;
public SipHeaderFieldValue(String value) {
int startPos = value.indexOf(RFC3261.RIGHT_ANGLE_BRACKET);
int pos;
if (startPos > -1) {
pos = value.indexOf(RFC3261.PARAM_SEPARATOR, startPos);
} else {
pos = value.indexOf(RFC3261.PARAM_SEPARATOR);
}
String paramsString;
if (pos > -1) {
this.value = value.substring(0,pos);
paramsString = value.substring(pos);
} else {
this.value = value;
paramsString = "";
}
params = new HashMap<SipHeaderParamName, String>();
if (paramsString.contains(RFC3261.PARAM_SEPARATOR)) {
String[] arr = paramsString.split(RFC3261.PARAM_SEPARATOR);
if (arr.length > 1) {
for (int i = 1; i < arr.length; ++i) {
String paramName = arr[i];
String paramValue = "";
pos = paramName.indexOf(RFC3261.PARAM_ASSIGNMENT);
if (pos > -1) {
paramName = arr[i].substring(0, pos);
paramValue = arr[i].substring(pos + 1);
}
params.put(new SipHeaderParamName(paramName), paramValue);
}
}
}
}
public String getParam(SipHeaderParamName name) {
return params.get(name);
}
public void addParam(SipHeaderParamName name, String value) {
params.put(name, value);
}
public void removeParam(SipHeaderParamName name) {
params.remove(name);
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
if (params == null || params.isEmpty()) {
return value;
}
StringBuffer buf = new StringBuffer(value);
for (SipHeaderParamName name: params.keySet()) {
buf.append(RFC3261.PARAM_SEPARATOR).append(name);
String value = params.get(name);
if (!"".equals(value.trim())) {
buf.append(RFC3261.PARAM_ASSIGNMENT).append(value);
}
}
return buf.toString();
}
}

View File

@@ -0,0 +1,56 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class SipHeaderParamName {
private String name;
public SipHeaderParamName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
String objName = ((SipHeaderParamName)obj).getName();
if (name.equalsIgnoreCase(objName)) {
return true;
}
return false;
}
@Override
public int hashCode() {
return name.toLowerCase().hashCode();
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,102 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.util.ArrayList;
import peers.sip.RFC3261;
public class SipHeaders {
private ArrayList<SipHeader> headers;
public SipHeaders() {
headers = new ArrayList<SipHeader>();
}
/**
*
* @param name
* @param value
* @param index -1 to add at the end
*/
public void add(SipHeaderFieldName name, SipHeaderFieldValue value, int index) {
SipHeader header = new SipHeader(name, value);
if (headers.contains(header)) {
header = headers.get(headers.indexOf(header));
SipHeaderFieldValue oldValue = header.getValue();
//TODO check is header can be multi valued
if (oldValue instanceof SipHeaderFieldMultiValue) {
SipHeaderFieldMultiValue oldMultiVal = (SipHeaderFieldMultiValue) oldValue;
oldMultiVal.getValues().add(value);
} else {
ArrayList<SipHeaderFieldValue> arr = new ArrayList<SipHeaderFieldValue>();
arr.add(oldValue);
arr.add(value);
header.setValue(new SipHeaderFieldMultiValue(arr));
}
} else {
if (index == -1) {
headers.add(header);
} else {
headers.add(index, header);
}
}
}
public void add(SipHeaderFieldName name, SipHeaderFieldValue value) {
add(name, value, -1);
}
@SuppressWarnings("unlikely-arg-type")
public void remove(SipHeaderFieldName name) {
headers.remove(name);
}
public boolean contains(SipHeaderFieldName name) {
return headers.contains(new SipHeader(name, null));
}
public SipHeaderFieldValue get(SipHeaderFieldName name) {
int index = headers.indexOf(new SipHeader(name, null));
if (index < 0) {
return null;
}
return headers.get(index).getValue();
}
public int getCount() {
return headers.size();
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
for (SipHeader header : headers) {
buf.append(header.getName().toString());
buf.append(": ");
buf.append(header.getValue());
buf.append(RFC3261.CRLF);
}
return buf.toString();
}
}

View File

@@ -0,0 +1,53 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.util.HashMap;
import peers.sip.RFC3261;
public class SipHeadersTable {
private HashMap<Character, String> headers;
/**
* should be instanciated only once, it was a singleton.
*/
public SipHeadersTable() {
headers = new HashMap<Character, String>();
//RFC 3261 Section 10
headers.put(RFC3261.COMPACT_HDR_CALLID, RFC3261.HDR_CALLID);
headers.put(RFC3261.COMPACT_HDR_CONTACT, RFC3261.HDR_CONTACT);
headers.put(RFC3261.COMPACT_HDR_CONTENT_ENCODING, RFC3261.HDR_CONTENT_ENCODING);
headers.put(RFC3261.COMPACT_HDR_CONTENT_LENGTH, RFC3261.HDR_CONTENT_LENGTH);
headers.put(RFC3261.COMPACT_HDR_CONTENT_TYPE, RFC3261.HDR_CONTENT_TYPE);
headers.put(RFC3261.COMPACT_HDR_FROM, RFC3261.HDR_FROM);
headers.put(RFC3261.COMPACT_HDR_SUBJECT, RFC3261.HDR_SUBJECT);
headers.put(RFC3261.COMPACT_HDR_SUPPORTED, RFC3261.HDR_SUPPORTED);
headers.put(RFC3261.COMPACT_HDR_TO, RFC3261.HDR_TO);
headers.put(RFC3261.COMPACT_HDR_VIA, RFC3261.HDR_VIA);
}
public String getLongForm(char compactForm) {
return headers.get(compactForm);
}
}

View File

@@ -0,0 +1,189 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import peers.sip.RFC3261;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
public class SipParser {
private BufferedReader reader;
private final static int BUFF_SIZE = 1024;
private List<SipHeaderFieldName> singleValueHeaders;
public SipParser() {
singleValueHeaders = new ArrayList<SipHeaderFieldName>();
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_WWW_AUTHENTICATE));
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_AUTHORIZATION));
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_PROXY_AUTHENTICATE));
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_PROXY_AUTHORIZATION));
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_SUPPORTED));
singleValueHeaders.add(new SipHeaderFieldName(
RFC3261.HDR_SUBJECT));
}
public synchronized SipMessage parse(InputStream in)
throws IOException, SipParserException {
InputStreamReader inputStreamReader = new InputStreamReader(in);
reader = new BufferedReader(inputStreamReader);
String startLine = reader.readLine();
while (startLine == null || startLine.equals("")) {
startLine = reader.readLine();
}
SipMessage sipMessage;
if (startLine.toUpperCase().startsWith(RFC3261.DEFAULT_SIP_VERSION)) {
sipMessage = parseSipResponse(startLine);
}
else {
sipMessage = parseSipRequest(startLine);
}
parseHeaders(sipMessage);
parseBody(sipMessage);
return sipMessage;
}
private SipRequest parseSipRequest(String startLine) throws SipParserException {
String[] params = startLine.split(" ");
if (params.length != 3) {
throw new SipParserException("invalid request line");
}
if (!RFC3261.DEFAULT_SIP_VERSION.equals(params[2].toUpperCase())) {
throw new SipParserException("unsupported SIP version");
}
SipURI requestUri;
try {
requestUri = new SipURI(params[1]);
} catch (SipUriSyntaxException e) {
throw new SipParserException(e);
}
return new SipRequest(params[0], requestUri);
}
private SipResponse parseSipResponse(String startLine) throws SipParserException {
String[] params = startLine.split(" ");
if (params.length < 3) {
throw new SipParserException("incorrect status line");
}
if (!RFC3261.DEFAULT_SIP_VERSION.equals(params[0].toUpperCase())) {
throw new SipParserException("unsupported SIP version");
}
StringBuffer buf = new StringBuffer();
for (int i = 2; i < params.length; ++i) {
buf.append(params[i]).append(" ");
}
buf.deleteCharAt(buf.length() - 1);
return new SipResponse(Integer.parseInt(params[1]), buf.toString());
}
private void parseHeaders(SipMessage sipMessage) throws IOException, SipParserException {
SipHeaders sipHeaders = new SipHeaders();
String headerLine = reader.readLine();
if (headerLine == null) {
throw new SipParserException(sipMessage.toString());
}
while (!"".equals(headerLine)) {
String nextLine = reader.readLine();
if (nextLine != null &&
(nextLine.startsWith(" ") || nextLine.startsWith("\t"))) {
StringBuffer buf = new StringBuffer(headerLine);
while (nextLine != null &&
(nextLine.startsWith(" ") || nextLine.startsWith("\t"))) {
buf.append(' ');
buf.append(nextLine.trim());
nextLine = reader.readLine();
}
headerLine = buf.toString();
}
if (headerLine == null) {
throw new SipParserException(sipMessage.toString());
}
int columnPos = headerLine.indexOf(RFC3261.FIELD_NAME_SEPARATOR);
if (columnPos < 0) {
throw new SipParserException("Invalid header line");
}
SipHeaderFieldName sipHeaderName = new SipHeaderFieldName(
headerLine.substring(0, columnPos).trim());
String value = headerLine.substring(columnPos + 1).trim();
SipHeaderFieldValue sipHeaderValue;
if (!singleValueHeaders.contains(sipHeaderName) &&
value.indexOf(RFC3261.HEADER_SEPARATOR) > -1) {
String[] values = value.split(RFC3261.HEADER_SEPARATOR);
List<SipHeaderFieldValue> list =
new ArrayList<SipHeaderFieldValue>();
for (String s: values) {
list.add(new SipHeaderFieldValue(s));
}
sipHeaderValue = new SipHeaderFieldMultiValue(list);
} else {
sipHeaderValue = new SipHeaderFieldValue(value);
}
sipHeaders.add(sipHeaderName, sipHeaderValue);
headerLine = nextLine;
}
sipMessage.setSipHeaders(sipHeaders);
}
public void parseBody(SipMessage sipMessage) throws IOException, SipParserException {
SipHeaderFieldValue contentLengthValue =
sipMessage.getSipHeaders().get(new SipHeaderFieldName(
RFC3261.HDR_CONTENT_LENGTH));
if (contentLengthValue == null) {
return;
}
int length = Integer.parseInt(contentLengthValue.toString());
byte[] buff = new byte[BUFF_SIZE];
int i;
int count = 0;
while (count < length && (i = reader.read()) != -1) {
if (count >= buff.length) {
byte[] aux = new byte[buff.length + BUFF_SIZE];
System.arraycopy(buff, 0, aux, 0, buff.length);
buff = aux;
}
buff[count++] = (byte)i;
}
if (count != buff.length) {
byte[] aux = new byte[count];
System.arraycopy(buff, 0, aux, 0, count);
buff = aux;
}
sipMessage.setBody(buff);
}
}

View File

@@ -0,0 +1,42 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class SipParserException extends Exception {
private static final long serialVersionUID = 1L;
public SipParserException() {
super();
}
public SipParserException(String message, Throwable cause) {
super(message, cause);
}
public SipParserException(String message) {
super(message);
}
public SipParserException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,136 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
import java.util.Hashtable;
import peers.sip.RFC3261;
public class SipURI {
public final static int DEFAULT_PORT = -1;
private String stringRepresentation;
/**
* telephone-subscriber and optional port are not managed
*/
private String userinfo;
private String host;
private int port = DEFAULT_PORT;
/**
* Use empty strings in value if the parameter has no value
*/
private Hashtable<String, String> uriParameters;
//headers not implemented
//private Hashtable<String, String> headers;
public SipURI(String sipUri)
throws SipUriSyntaxException {
stringRepresentation = sipUri;
StringBuffer buf = new StringBuffer(sipUri);
String scheme = RFC3261.SIP_SCHEME + RFC3261.SCHEME_SEPARATOR;
if (!sipUri.startsWith(scheme)) {
throw new SipUriSyntaxException("SIP URI must start with " + scheme);
}
buf.delete(0, scheme.length());
int atPos = buf.indexOf("@");
if (atPos == 0) {
throw new SipUriSyntaxException("userinfo cannot start with a '@'");
}
if (atPos > 0) {
userinfo = buf.substring(0, atPos);
buf.delete(0, atPos + 1);
}
int endHostport = buf.indexOf(";");
if (endHostport == 0) {
throw new SipUriSyntaxException("hostport not present or it cannot start with ';'");
}
if (endHostport < 0) {
endHostport = buf.length();
}
String hostport = buf.substring(0, endHostport);
buf.delete(0, endHostport);
int colonPos = hostport.indexOf(':');
if (colonPos > -1) {
if (colonPos == hostport.length() - 1) {
throw new SipUriSyntaxException("hostport cannot terminate with a ':'");
}
port = Integer.parseInt(hostport.substring(colonPos + 1));
} else {
colonPos = hostport.length();
}
host = hostport.substring(0, colonPos);
if (buf.length() == 1) {
//if there is only one ';' at the end of the uri => do not
//parse uri-parameters and headers
buf.deleteCharAt(0);
}
if (buf.length() <= 0) {
return;
}
uriParameters = new Hashtable<String, String>();
while (buf.length() > 0) {
buf.deleteCharAt(0);//delete the first ';'
int nextSemicolon = buf.indexOf(";");
if (nextSemicolon < 0) {
nextSemicolon = buf.length();
}
int nextEquals = buf.indexOf("=");
if (nextEquals < 0) {
nextEquals = nextSemicolon;
}
if (nextEquals > nextSemicolon) {
nextEquals = nextSemicolon;
}
int afterEquals;
if (nextEquals + 1 > nextSemicolon) {
afterEquals = nextSemicolon;
} else {
afterEquals = nextEquals + 1;
}
uriParameters.put(buf.substring(0, nextEquals), buf.substring(afterEquals, nextSemicolon));
buf.delete(0, nextSemicolon);
}
}
@Override
public String toString() {
return stringRepresentation;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public Hashtable<String, String> getUriParameters() {
return uriParameters;
}
public String getUserinfo() {
return userinfo;
}
}

View File

@@ -0,0 +1,43 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.syntaxencoding;
public class SipUriSyntaxException extends Exception {
private static final long serialVersionUID = 1L;
public SipUriSyntaxException() {
super();
}
public SipUriSyntaxException(String message, Throwable cause) {
super(message, cause);
}
public SipUriSyntaxException(String message) {
super(message);
}
public SipUriSyntaxException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,29 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.transport.SipResponse;
public interface ClientTransaction {
void receivedResponse(SipResponse sipResponse);
void start();
String getContact();
}

View File

@@ -0,0 +1,31 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.transport.SipResponse;
public interface ClientTransactionUser {
public void transactionTimeout(ClientTransaction clientTransaction);
public void provResponseReceived(SipResponse sipResponse, Transaction transaction);
//TODO eventually pass transaction to the transaction user
public void errResponseReceived(SipResponse sipResponse);//3XX is considered as an error response
public void successResponseReceived(SipResponse sipResponse, Transaction transaction);
public void transactionTransportError();
}

View File

@@ -0,0 +1,247 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transport.MessageSender;
import peers.sip.transport.SipClientTransportUser;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class InviteClientTransaction extends InviteTransaction
implements ClientTransaction, SipClientTransportUser {
public final InviteClientTransactionState INIT;
public final InviteClientTransactionState CALLING;
public final InviteClientTransactionState PROCEEDING;
public final InviteClientTransactionState COMPLETED;
public final InviteClientTransactionState TERMINATED;
protected ClientTransactionUser transactionUser;
protected String transport;
private InviteClientTransactionState state;
//private SipClientTransport sipClientTransport;
private MessageSender messageSender;
private int nbRetrans;
private SipRequest ack;
private int remotePort;
private InetAddress remoteInetAddress;
InviteClientTransaction(String branchId, InetAddress inetAddress,
int port, String transport, SipRequest sipRequest,
ClientTransactionUser transactionUser, Timer timer,
TransportManager transportManager,
TransactionManager transactionManager) {
super(branchId, timer, transportManager, transactionManager);
this.transport = transport;
SipHeaderFieldValue via = new SipHeaderFieldValue("");
via.addParam(new SipHeaderParamName(RFC3261.PARAM_BRANCH), branchId);
sipRequest.getSipHeaders().add(new SipHeaderFieldName(RFC3261.HDR_VIA), via, 0);
nbRetrans = 0;
INIT = new InviteClientTransactionStateInit(getId(), this);
state = INIT;
CALLING = new InviteClientTransactionStateCalling(getId(), this);
PROCEEDING = new InviteClientTransactionStateProceeding(getId(), this);
COMPLETED = new InviteClientTransactionStateCompleted(getId(), this);
TERMINATED = new InviteClientTransactionStateTerminated(getId(), this);
//17.1.1.2
request = sipRequest;
this.transactionUser = transactionUser;
remotePort = port;
remoteInetAddress = inetAddress;
try {
messageSender = transportManager.createClientTransport(
request, remoteInetAddress, remotePort, transport);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
}
public void setState(InviteClientTransactionState state) {
this.state.log(state);
this.state = state;
if(TERMINATED.equals(state)) {
//transactionManager.removeClientTransaction(branchId, method);
transactionManager = null;
}
}
public void start() {
state.start();
//send request using transport information and sipRequest
// try {
// sipClientTransport = SipTransportFactory.getInstance()
// .createClientTransport(this, request, remoteInetAddress,
// remotePort, transport);
// sipClientTransport.send(request);
// } catch (IOException e) {
// //e.printStackTrace();
// transportError();
// }
try {
messageSender.sendMessage(request);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
Logger.debug("InviteClientTransaction.start");
if (RFC3261.TRANSPORT_UDP.equals(transport)) {
//start timer A with value T1 for retransmission
timer.schedule(new TimerA(), RFC3261.TIMER_T1);
}
//TODO start timer B with value 64*T1 for transaction timeout
timer.schedule(new TimerB(), 64 * RFC3261.TIMER_T1);
}
public synchronized void receivedResponse(SipResponse sipResponse) {
responses.add(sipResponse);
// 17.1.1
int statusCode = sipResponse.getStatusCode();
if (statusCode < RFC3261.CODE_MIN_PROV) {
Logger.error("invalid response code");
} else if (statusCode < RFC3261.CODE_MIN_SUCCESS) {
state.received1xx();
} else if (statusCode < RFC3261.CODE_MIN_REDIR) {
state.received2xx();
} else if (statusCode <= RFC3261.CODE_MAX) {
state.received300To699();
} else {
Logger.error("invalid response code");
}
}
public void transportError() {
state.transportError();
}
void createAndSendAck() {
//p.126 last paragraph
//17.1.1.3
ack = new SipRequest(RFC3261.METHOD_ACK, request.getRequestUri());
SipHeaderFieldValue topVia = Utils.getTopVia(request);
SipHeaders ackSipHeaders = ack.getSipHeaders();
ackSipHeaders.add(new SipHeaderFieldName(RFC3261.HDR_VIA), topVia);
Utils.copyHeader(request, ack, RFC3261.HDR_CALLID);
Utils.copyHeader(request, ack, RFC3261.HDR_FROM);
Utils.copyHeader(getLastResponse(), ack, RFC3261.HDR_TO);
//TODO what happens if a prov response is received after a 200+ ...
SipHeaders requestSipHeaders = request.getSipHeaders();
SipHeaderFieldName cseqName = new SipHeaderFieldName(RFC3261.HDR_CSEQ);
SipHeaderFieldValue cseq = requestSipHeaders.get(cseqName);
cseq.setValue(cseq.toString().replace(RFC3261.METHOD_INVITE, RFC3261.METHOD_ACK));
ackSipHeaders.add(cseqName, cseq);
Utils.copyHeader(request, ack, RFC3261.HDR_ROUTE);
sendAck();
}
void sendAck() {
//ack is passed to the transport layer...
//TODO manage ACK retrans
//sipClientTransport.send(ack);
try {
messageSender.sendMessage(ack);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
}
void sendRetrans() {
++nbRetrans;
//sipClientTransport.send(request);
try {
messageSender.sendMessage(request);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
timer.schedule(new TimerA(), (long)Math.pow(2, nbRetrans) * RFC3261.TIMER_T1);
}
public void requestTransportError(SipRequest sipRequest, Exception e) {
// TODO Auto-generated method stub
}
public void responseTransportError(Exception e) {
// TODO Auto-generated method stub
}
class TimerA extends TimerTask {
@Override
public void run() {
state.timerAFires();
}
}
class TimerB extends TimerTask {
@Override
public void run() {
state.timerBFires();
}
}
class TimerD extends TimerTask {
@Override
public void run() {
state.timerDFires();
}
}
public String getContact() {
if (messageSender != null) {
return messageSender.getContact();
}
return null;
}
}

View File

@@ -0,0 +1,45 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.AbstractState;
public abstract class InviteClientTransactionState extends AbstractState {
protected InviteClientTransaction inviteClientTransaction;
public InviteClientTransactionState(String id,
InviteClientTransaction inviteClientTransaction) {
super(id);
this.inviteClientTransaction = inviteClientTransaction;
}
public void start() {}
public void timerAFires() {}
public void timerBFires() {}
public void received2xx() {}
public void received1xx() {}
public void received300To699() {}
public void transportError() {}
public void timerDFires() {}
}

View File

@@ -0,0 +1,82 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteClientTransactionStateCalling extends InviteClientTransactionState {
public InviteClientTransactionStateCalling(String id,
InviteClientTransaction inviteClientTransaction) {
super(id, inviteClientTransaction);
}
@Override
public void timerAFires() {
InviteClientTransactionState nextState = inviteClientTransaction.CALLING;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.sendRetrans();
}
@Override
public void timerBFires() {
timerBFiresOrTransportError();
}
@Override
public void transportError() {
timerBFiresOrTransportError();
}
private void timerBFiresOrTransportError() {
InviteClientTransactionState nextState = inviteClientTransaction.TERMINATED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.transactionTimeout(
inviteClientTransaction);
}
@Override
public void received2xx() {
InviteClientTransactionState nextState = inviteClientTransaction.TERMINATED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.successResponseReceived(
inviteClientTransaction.getLastResponse(), inviteClientTransaction);
}
@Override
public void received1xx() {
InviteClientTransactionState nextState = inviteClientTransaction.PROCEEDING;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.provResponseReceived(
inviteClientTransaction.getLastResponse(), inviteClientTransaction);
}
@Override
public void received300To699() {
InviteClientTransactionState nextState = inviteClientTransaction.COMPLETED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.createAndSendAck();
inviteClientTransaction.transactionUser.errResponseReceived(
inviteClientTransaction.getLastResponse());
}
}

View File

@@ -0,0 +1,59 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
public class InviteClientTransactionStateCompleted extends
InviteClientTransactionState {
public InviteClientTransactionStateCompleted(String id,
InviteClientTransaction inviteClientTransaction) {
super(id, inviteClientTransaction);
int delay = 0;
if (RFC3261.TRANSPORT_UDP.equals(inviteClientTransaction.transport)) {
delay = RFC3261.TIMER_INVITE_CLIENT_TRANSACTION;
}
inviteClientTransaction.timer.schedule(inviteClientTransaction.new TimerD(), delay);
}
@Override
public void received300To699() {
InviteClientTransactionState nextState = inviteClientTransaction.COMPLETED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.sendAck();
}
@Override
public void transportError() {
InviteClientTransactionState nextState = inviteClientTransaction.TERMINATED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.transactionTransportError();
}
@Override
public void timerDFires() {
InviteClientTransactionState nextState = inviteClientTransaction.TERMINATED;
inviteClientTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,37 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteClientTransactionStateInit extends InviteClientTransactionState {
public InviteClientTransactionStateInit(String id,
InviteClientTransaction inviteClientTransaction) {
super(id, inviteClientTransaction);
}
@Override
public void start() {
InviteClientTransactionState nextState = inviteClientTransaction.CALLING;
inviteClientTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,58 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteClientTransactionStateProceeding extends
InviteClientTransactionState {
public InviteClientTransactionStateProceeding(String id,
InviteClientTransaction inviteClientTransaction) {
super(id, inviteClientTransaction);
}
@Override
public void received1xx() {
InviteClientTransactionState nextState = inviteClientTransaction.PROCEEDING;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.provResponseReceived(
inviteClientTransaction.getLastResponse(), inviteClientTransaction);
}
@Override
public void received2xx() {
InviteClientTransactionState nextState = inviteClientTransaction.TERMINATED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.transactionUser.successResponseReceived(
inviteClientTransaction.getLastResponse(), inviteClientTransaction);
}
@Override
public void received300To699() {
InviteClientTransactionState nextState = inviteClientTransaction.COMPLETED;
inviteClientTransaction.setState(nextState);
inviteClientTransaction.createAndSendAck();
inviteClientTransaction.transactionUser.errResponseReceived(
inviteClientTransaction.getLastResponse());
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteClientTransactionStateTerminated extends
InviteClientTransactionState {
public InviteClientTransactionStateTerminated(String id,
InviteClientTransaction inviteClientTransaction) {
super(id, inviteClientTransaction);
}
}

View File

@@ -0,0 +1,179 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.SipServerTransportUser;
import peers.sip.transport.TransportManager;
public class InviteServerTransaction extends InviteTransaction
implements ServerTransaction, SipServerTransportUser {
public final InviteServerTransactionState INIT;
public final InviteServerTransactionState PROCEEDING;
public final InviteServerTransactionState COMPLETED;
public final InviteServerTransactionState CONFIRMED;
public final InviteServerTransactionState TERMINATED;
protected String transport;
protected int nbRetrans;
protected ServerTransactionUser serverTransactionUser;
private InviteServerTransactionState state;
//private SipServerTransport sipServerTransport;
private int port;
InviteServerTransaction(String branchId, int port, String transport,
SipResponse sipResponse, ServerTransactionUser serverTransactionUser,
SipRequest sipRequest, Timer timer, TransactionManager transactionManager,
TransportManager transportManager) {
super(branchId, timer, transportManager, transactionManager);
INIT = new InviteServerTransactionStateInit(getId(), this);
state = INIT;
PROCEEDING = new InviteServerTransactionStateProceeding(getId(), this);
COMPLETED = new InviteServerTransactionStateCompleted(getId(), this);
CONFIRMED = new InviteServerTransactionStateConfirmed(getId(), this);
TERMINATED = new InviteServerTransactionStateTerminated(getId(), this);
this.request = sipRequest;
this.port = port;
this.transport = transport;
responses.add(sipResponse);
nbRetrans = 0;
this.serverTransactionUser = serverTransactionUser;
//TODO pass INV to TU, send 100 if TU won't in 200ms
}
public void start() {
state.start();
// sipServerTransport = SipTransportFactory.getInstance()
// .createServerTransport(this, port, transport);
try {
transportManager.createServerTransport(transport, port);
} catch (IOException e) {
Logger.error("input/output error", e);
}
}
public void receivedRequest(SipRequest sipRequest) {
String method = sipRequest.getMethod();
if (RFC3261.METHOD_INVITE.equals(method)) {
state.receivedInvite();
} else {
// if not INVITE, we consider that a ACK is received
// in the case the call was not successful
state.receivedAck();
}
}
public void sendReponse(SipResponse sipResponse) {
//TODO check that a retransmission response will be considered as
//equal (for contains) to the first response
if (!responses.contains(sipResponse)) {
responses.add(sipResponse);
}
int statusCode = sipResponse.getStatusCode();
if (statusCode == RFC3261.CODE_MIN_PROV) {
// TODO 100 trying
} else if (statusCode < RFC3261.CODE_MIN_SUCCESS) {
state.received101To199();
} else if (statusCode < RFC3261.CODE_MIN_REDIR) {
state.received2xx();
} else if (statusCode <= RFC3261.CODE_MAX) {
state.received300To699();
} else {
Logger.error("invalid response code");
}
}
public void setState(InviteServerTransactionState state) {
this.state.log(state);
this.state = state;
}
public void messageReceived(SipMessage sipMessage) {
// TODO Auto-generated method stub
}
void sendLastResponse() {
//sipServerTransport.sendResponse(responses.get(responses.size() - 1));
int nbOfResponses = responses.size();
if (nbOfResponses > 0) {
try {
transportManager.sendResponse(responses.get(nbOfResponses - 1));
} catch (IOException e) {
Logger.error("input/output error", e);
}
}
}
public SipResponse getLastResponse() {
int nbOfResponses = responses.size();
if (nbOfResponses > 0) {
return responses.get(nbOfResponses - 1);
}
return null;
}
// TODO send provional response
/*
* maybe the 200 response mechanism could be retrieved for 1xx responses.
*/
// void stopSipServerTransport() {
// sipServerTransport.stop();
// }
class TimerG extends TimerTask {
@Override
public void run() {
state.timerGFires();
}
}
class TimerH extends TimerTask {
@Override
public void run() {
state.timerHFiresOrTransportError();
}
}
class TimerI extends TimerTask {
@Override
public void run() {
state.timerIFires();
}
}
}

View File

@@ -0,0 +1,46 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.AbstractState;
public abstract class InviteServerTransactionState extends AbstractState {
protected InviteServerTransaction inviteServerTransaction;
public InviteServerTransactionState(String id,
InviteServerTransaction inviteServerTransaction) {
super(id);
this.inviteServerTransaction = inviteServerTransaction;
}
public void start() {}
public void receivedInvite() {}
public void received101To199() {}
public void transportError() {}
public void received2xx() {}
public void received300To699() {}
public void timerGFires() {}
public void timerHFiresOrTransportError() {}
public void receivedAck() {}
public void timerIFires() {}
}

View File

@@ -0,0 +1,74 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
public class InviteServerTransactionStateCompleted extends
InviteServerTransactionState {
public InviteServerTransactionStateCompleted(String id,
InviteServerTransaction inviteServerTransaction) {
super(id, inviteServerTransaction);
}
@Override
public void timerGFires() {
InviteServerTransactionState nextState = inviteServerTransaction.COMPLETED;
inviteServerTransaction.setState(nextState);
inviteServerTransaction.sendLastResponse();
long delay = (long)Math.pow(2,
++inviteServerTransaction.nbRetrans) * RFC3261.TIMER_T1;
inviteServerTransaction.timer.schedule(
inviteServerTransaction.new TimerG(),
Math.min(delay, RFC3261.TIMER_T2));
}
@Override
public void timerHFiresOrTransportError() {
InviteServerTransactionState nextState = inviteServerTransaction.TERMINATED;
inviteServerTransaction.setState(nextState);
inviteServerTransaction.serverTransactionUser.transactionFailure();
}
@Override
public void receivedAck() {
InviteServerTransactionState nextState = inviteServerTransaction.CONFIRMED;
inviteServerTransaction.setState(nextState);
int delay;
if (RFC3261.TRANSPORT_UDP.equals(inviteServerTransaction.transport)) {
delay = RFC3261.TIMER_T4;
} else {
delay = 0;
}
inviteServerTransaction.timer.schedule(
inviteServerTransaction.new TimerI(), delay);
}
@Override
public void receivedInvite() {
InviteServerTransactionState nextState = inviteServerTransaction.COMPLETED;
inviteServerTransaction.setState(nextState);
// retransmission
inviteServerTransaction.sendLastResponse();
}
}

View File

@@ -0,0 +1,45 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteServerTransactionStateConfirmed extends
InviteServerTransactionState {
public InviteServerTransactionStateConfirmed(String id,
InviteServerTransaction inviteServerTransaction) {
super(id, inviteServerTransaction);
}
@Override
public void timerIFires() {
InviteServerTransactionState nextState =
inviteServerTransaction.TERMINATED;
inviteServerTransaction.setState(nextState);
// TODO destroy invite server transaction immediately
// (dereference it in transaction manager serverTransactions hashtable)
inviteServerTransaction.transactionManager.removeServerTransaction(
inviteServerTransaction.branchId,
inviteServerTransaction.method);
}
}

View File

@@ -0,0 +1,37 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteServerTransactionStateInit extends
InviteServerTransactionState {
public InviteServerTransactionStateInit(String id,
InviteServerTransaction inviteServerTransaction) {
super(id, inviteServerTransaction);
}
@Override
public void start() {
InviteServerTransactionState nextState = inviteServerTransaction.PROCEEDING;
inviteServerTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,74 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
public class InviteServerTransactionStateProceeding extends
InviteServerTransactionState {
public InviteServerTransactionStateProceeding(String id,
InviteServerTransaction inviteServerTransaction) {
super(id, inviteServerTransaction);
}
@Override
public void received101To199() {
InviteServerTransactionState nextState = inviteServerTransaction.PROCEEDING;
inviteServerTransaction.setState(nextState);
//TODO inviteServerTransaction.sendProvisionalResponse();
inviteServerTransaction.sendLastResponse();
}
@Override
public void transportError() {
InviteServerTransactionState nextState = inviteServerTransaction.TERMINATED;
inviteServerTransaction.setState(nextState);
}
@Override
public void received2xx() {
InviteServerTransactionState nextState = inviteServerTransaction.TERMINATED;
inviteServerTransaction.setState(nextState);
inviteServerTransaction.sendLastResponse();
}
@Override
public void received300To699() {
InviteServerTransactionState nextState = inviteServerTransaction.COMPLETED;
inviteServerTransaction.setState(nextState);
inviteServerTransaction.sendLastResponse();
if (RFC3261.TRANSPORT_UDP.equals(inviteServerTransaction.transport)) {
inviteServerTransaction.timer.schedule(
inviteServerTransaction.new TimerG(), RFC3261.TIMER_T1);
}
inviteServerTransaction.timer.schedule(
inviteServerTransaction.new TimerH(), 64 * RFC3261.TIMER_T1);
}
@Override
public void receivedInvite() {
InviteServerTransactionState nextState = inviteServerTransaction.PROCEEDING;
inviteServerTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class InviteServerTransactionStateTerminated extends
InviteServerTransactionState {
public InviteServerTransactionStateTerminated(String id,
InviteServerTransaction inviteServerTransaction) {
super(id, inviteServerTransaction);
}
}

View File

@@ -0,0 +1,37 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.util.Timer;
import peers.sip.RFC3261;
import peers.sip.transport.TransportManager;
public abstract class InviteTransaction extends Transaction {
protected InviteTransaction(String branchId, Timer timer,
TransportManager transportManager,
TransactionManager transactionManager) {
super(branchId, RFC3261.METHOD_INVITE, timer, transportManager,
transactionManager);
}
}

View File

@@ -0,0 +1,202 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.transport.MessageSender;
import peers.sip.transport.SipClientTransportUser;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class NonInviteClientTransaction extends NonInviteTransaction
implements ClientTransaction, SipClientTransportUser {
public final NonInviteClientTransactionState INIT;
public final NonInviteClientTransactionState TRYING;
public final NonInviteClientTransactionState PROCEEDING;
public final NonInviteClientTransactionState COMPLETED;
public final NonInviteClientTransactionState TERMINATED;
protected ClientTransactionUser transactionUser;
protected String transport;
protected int nbRetrans;
private NonInviteClientTransactionState state;
//private SipClientTransport sipClientTransport;
private MessageSender messageSender;
private int remotePort;
private InetAddress remoteInetAddress;
NonInviteClientTransaction(String branchId, InetAddress inetAddress,
int port, String transport, SipRequest sipRequest,
ClientTransactionUser transactionUser, Timer timer,
TransportManager transportManager,
TransactionManager transactionManager) {
super(branchId, sipRequest.getMethod(), timer, transportManager,
transactionManager);
this.transport = transport;
SipHeaderFieldValue via = new SipHeaderFieldValue("");
via.addParam(new SipHeaderParamName(RFC3261.PARAM_BRANCH), branchId);
sipRequest.getSipHeaders().add(new SipHeaderFieldName(RFC3261.HDR_VIA), via, 0);
nbRetrans = 0;
INIT = new NonInviteClientTransactionStateInit(getId(), this);
state = INIT;
TRYING = new NonInviteClientTransactionStateTrying(getId(), this);
PROCEEDING = new NonInviteClientTransactionStateProceeding(getId(),
this);
COMPLETED = new NonInviteClientTransactionStateCompleted(getId(),
this);
TERMINATED = new NonInviteClientTransactionStateTerminated(getId(),
this);
request = sipRequest;
this.transactionUser = transactionUser;
remotePort = port;
remoteInetAddress = inetAddress;
try {
messageSender = transportManager.createClientTransport(
request, remoteInetAddress, remotePort, transport);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
//TODO send request
}
public void setState(NonInviteClientTransactionState state) {
this.state.log(state);
this.state = state;
}
public void start() {
state.start();
//17.1.2.2
// try {
// sipClientTransport = SipTransportFactory.getInstance()
// .createClientTransport(this, request, remoteInetAddress,
// remotePort, transport);
// sipClientTransport.send(request);
// } catch (IOException e) {
// //e.printStackTrace();
// transportError();
// }
try {
messageSender.sendMessage(request);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
if (RFC3261.TRANSPORT_UDP.equals(transport)) {
//start timer E with value T1 for retransmission
timer.schedule(new TimerE(), RFC3261.TIMER_T1);
}
timer.schedule(new TimerF(), 64 * RFC3261.TIMER_T1);
}
void sendRetrans(long delay) {
//sipClientTransport.send(request);
try {
messageSender.sendMessage(request);
} catch (IOException e) {
Logger.error("input/output error", e);
transportError();
}
timer.schedule(new TimerE(), delay);
}
public void transportError() {
state.transportError();
}
public synchronized void receivedResponse(SipResponse sipResponse) {
responses.add(sipResponse);
// 17.1.1
int statusCode = sipResponse.getStatusCode();
if (statusCode < RFC3261.CODE_MIN_PROV) {
Logger.error("invalid response code");
} else if (statusCode < RFC3261.CODE_MIN_SUCCESS) {
state.received1xx();
} else if (statusCode <= RFC3261.CODE_MAX) {
state.received200To699();
} else {
Logger.error("invalid response code");
}
}
public void requestTransportError(SipRequest sipRequest, Exception e) {
// TODO Auto-generated method stub
}
public void responseTransportError(Exception e) {
// TODO Auto-generated method stub
}
class TimerE extends TimerTask {
@Override
public void run() {
state.timerEFires();
}
}
class TimerF extends TimerTask {
@Override
public void run() {
state.timerFFires();
}
}
class TimerK extends TimerTask {
@Override
public void run() {
state.timerKFires();
}
}
public String getContact() {
if (messageSender != null) {
return messageSender.getContact();
}
return null;
}
}

View File

@@ -0,0 +1,43 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.AbstractState;
public abstract class NonInviteClientTransactionState extends AbstractState {
protected NonInviteClientTransaction nonInviteClientTransaction;
public NonInviteClientTransactionState(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id);
this.nonInviteClientTransaction = nonInviteClientTransaction;
}
public void start() {}
public void timerEFires() {}
public void timerFFires() {}
public void transportError() {}
public void received1xx() {}
public void received200To699() {}
public void timerKFires() {}
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
public class NonInviteClientTransactionStateCompleted extends
NonInviteClientTransactionState {
public NonInviteClientTransactionStateCompleted(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id, nonInviteClientTransaction);
int delay = 0;
if (RFC3261.TRANSPORT_UDP.equals(
nonInviteClientTransaction.transport)) {
delay = RFC3261.TIMER_T4;
}
nonInviteClientTransaction.timer.schedule(
nonInviteClientTransaction.new TimerK(), delay);
}
@Override
public void timerKFires() {
NonInviteClientTransactionState nextState =
nonInviteClientTransaction.TERMINATED;
nonInviteClientTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,37 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class NonInviteClientTransactionStateInit extends
NonInviteClientTransactionState {
public NonInviteClientTransactionStateInit(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id, nonInviteClientTransaction);
}
@Override
public void start() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.TRYING;
nonInviteClientTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,80 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
import peers.sip.transport.SipResponse;
public class NonInviteClientTransactionStateProceeding extends
NonInviteClientTransactionState {
public NonInviteClientTransactionStateProceeding(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id, nonInviteClientTransaction);
}
@Override
public void timerEFires() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.PROCEEDING;
nonInviteClientTransaction.setState(nextState);
++nonInviteClientTransaction.nbRetrans;
nonInviteClientTransaction.sendRetrans(RFC3261.TIMER_T2);
}
@Override
public void timerFFires() {
timerFFiresOrTransportError();
}
@Override
public void transportError() {
timerFFiresOrTransportError();
}
private void timerFFiresOrTransportError() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.TERMINATED;
nonInviteClientTransaction.setState(nextState);
nonInviteClientTransaction.transactionUser.transactionTimeout(
nonInviteClientTransaction);
}
@Override
public void received1xx() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.PROCEEDING;
nonInviteClientTransaction.setState(nextState);
}
@Override
public void received200To699() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.COMPLETED;
nonInviteClientTransaction.setState(nextState);
SipResponse response = nonInviteClientTransaction.getLastResponse();
int code = response.getStatusCode();
if (code < RFC3261.CODE_MIN_REDIR) {
nonInviteClientTransaction.transactionUser.successResponseReceived(
response, nonInviteClientTransaction);
} else {
nonInviteClientTransaction.transactionUser.errResponseReceived(
response);
}
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class NonInviteClientTransactionStateTerminated extends
NonInviteClientTransactionState {
public NonInviteClientTransactionStateTerminated(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id, nonInviteClientTransaction);
}
}

View File

@@ -0,0 +1,83 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
import peers.sip.transport.SipResponse;
public class NonInviteClientTransactionStateTrying extends
NonInviteClientTransactionState {
public NonInviteClientTransactionStateTrying(String id,
NonInviteClientTransaction nonInviteClientTransaction) {
super(id, nonInviteClientTransaction);
}
@Override
public void timerEFires() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.TRYING;
nonInviteClientTransaction.setState(nextState);
long delay = (long)Math.pow(2,
++nonInviteClientTransaction.nbRetrans) * RFC3261.TIMER_T1;
nonInviteClientTransaction.sendRetrans(Math.min(delay, RFC3261.TIMER_T2));
}
@Override
public void timerFFires() {
timerFFiresOrTransportError();
}
@Override
public void transportError() {
timerFFiresOrTransportError();
}
private void timerFFiresOrTransportError() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.TERMINATED;
nonInviteClientTransaction.setState(nextState);
nonInviteClientTransaction.transactionUser.transactionTimeout(
nonInviteClientTransaction);
}
@Override
public void received1xx() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.PROCEEDING;
nonInviteClientTransaction.setState(nextState);
nonInviteClientTransaction.transactionUser.provResponseReceived(
nonInviteClientTransaction.getLastResponse(), nonInviteClientTransaction);
}
@Override
public void received200To699() {
NonInviteClientTransactionState nextState = nonInviteClientTransaction.COMPLETED;
nonInviteClientTransaction.setState(nextState);
SipResponse response = nonInviteClientTransaction.getLastResponse();
int code = response.getStatusCode();
if (code < RFC3261.CODE_MIN_REDIR) {
nonInviteClientTransaction.transactionUser.successResponseReceived(
response, nonInviteClientTransaction);
} else {
nonInviteClientTransaction.transactionUser.errResponseReceived(
response);
}
}
}

View File

@@ -0,0 +1,126 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class NonInviteServerTransaction extends NonInviteTransaction
implements ServerTransaction/*, SipServerTransportUser*/ {
public final NonInviteServerTransactionState TRYING;
public final NonInviteServerTransactionState PROCEEDING;
public final NonInviteServerTransactionState COMPLETED;
public final NonInviteServerTransactionState TERMINATED;
protected ServerTransactionUser serverTransactionUser;
protected Timer timer;
protected String transport;
private NonInviteServerTransactionState state;
//private int port;
NonInviteServerTransaction(String branchId, int port, String transport,
String method, ServerTransactionUser serverTransactionUser,
SipRequest sipRequest, Timer timer, TransportManager transportManager,
TransactionManager transactionManager) {
super(branchId, method, timer, transportManager, transactionManager);
TRYING = new NonInviteServerTransactionStateTrying(getId(), this);
state = TRYING;
PROCEEDING = new NonInviteServerTransactionStateProceeding(getId(),
this);
COMPLETED = new NonInviteServerTransactionStateCompleted(getId(), this);
TERMINATED = new NonInviteServerTransactionStateTerminated(getId(),
this);
//this.port = port;
this.transport = transport;
this.serverTransactionUser = serverTransactionUser;
request = sipRequest;
// sipServerTransport = SipTransportFactory.getInstance()
// .createServerTransport(this, port, transport);
try {
transportManager.createServerTransport(transport, port);
} catch (IOException e) {
Logger.error("input/output error", e);
}
//TODO pass request to TU
}
public void setState(NonInviteServerTransactionState state) {
this.state.log(state);
this.state = state;
}
public void receivedRequest(SipRequest sipRequest) {
state.receivedRequest();
}
public void sendReponse(SipResponse sipResponse) {
responses.add(sipResponse);
int statusCode = sipResponse.getStatusCode();
if (statusCode < RFC3261.CODE_200_OK) {
state.received1xx();
} else if (statusCode <= RFC3261.CODE_MAX) {
state.received200To699();
}
}
void sendLastResponse() {
//sipServerTransport.sendResponse(responses.get(responses.size() - 1));
int nbOfResponses = responses.size();
if (nbOfResponses > 0) {
try {
transportManager.sendResponse(responses.get(nbOfResponses - 1));
} catch (IOException e) {
Logger.error("input/output error", e);
}
}
}
public void start() {
// TODO Auto-generated method stub
}
// public void messageReceived(SipMessage sipMessage) {
// // TODO Auto-generated method stub
//
// }
class TimerJ extends TimerTask {
@Override
public void run() {
state.timerJFires();
}
}
}

View File

@@ -0,0 +1,42 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.AbstractState;
//17.2.2
public abstract class NonInviteServerTransactionState extends AbstractState {
protected NonInviteServerTransaction nonInviteServerTransaction;
public NonInviteServerTransactionState(String id,
NonInviteServerTransaction nonInviteServerTransaction) {
super(id);
this.nonInviteServerTransaction = nonInviteServerTransaction;
}
public void received200To699() {}
public void received1xx() {}
public void receivedRequest() {}
public void transportError() {}
public void timerJFires() {}
}

View File

@@ -0,0 +1,51 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class NonInviteServerTransactionStateCompleted extends
NonInviteServerTransactionState {
public NonInviteServerTransactionStateCompleted(String id,
NonInviteServerTransaction nonInviteServerTransaction) {
super(id, nonInviteServerTransaction);
}
@Override
public void timerJFires() {
NonInviteServerTransactionState nextState = nonInviteServerTransaction.TERMINATED;
nonInviteServerTransaction.setState(nextState);
}
@Override
public void transportError() {
NonInviteServerTransactionState nextState = nonInviteServerTransaction.TERMINATED;
nonInviteServerTransaction.setState(nextState);
}
@Override
public void receivedRequest() {
NonInviteServerTransactionState nextState = nonInviteServerTransaction.COMPLETED;
nonInviteServerTransaction.setState(nextState);
nonInviteServerTransaction.sendLastResponse();
}
}

View File

@@ -0,0 +1,72 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.RFC3261;
public class NonInviteServerTransactionStateProceeding extends
NonInviteServerTransactionState {
public NonInviteServerTransactionStateProceeding(String id,
NonInviteServerTransaction nonInviteServerTransaction) {
super(id, nonInviteServerTransaction);
}
@Override
public void received1xx() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.PROCEEDING;
nonInviteServerTransaction.setState(nextState);
nonInviteServerTransaction.sendLastResponse();
}
@Override
public void received200To699() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.COMPLETED;
nonInviteServerTransaction.setState(nextState);
nonInviteServerTransaction.sendLastResponse();
int timeout;
if (RFC3261.TRANSPORT_UDP.equals(
nonInviteServerTransaction.transport)) {
timeout = 64 * RFC3261.TIMER_T1;
} else {
timeout = 0;
}
nonInviteServerTransaction.timer.schedule(
nonInviteServerTransaction.new TimerJ(), timeout);
}
@Override
public void transportError() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.TERMINATED;
nonInviteServerTransaction.setState(nextState);
}
@Override
public void receivedRequest() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.PROCEEDING;
nonInviteServerTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class NonInviteServerTransactionStateTerminated extends
NonInviteServerTransactionState {
public NonInviteServerTransactionStateTerminated(String id,
NonInviteServerTransaction nonInviteServerTransaction) {
super(id, nonInviteServerTransaction);
}
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class NonInviteServerTransactionStateTrying extends
NonInviteServerTransactionState {
public NonInviteServerTransactionStateTrying(String id,
NonInviteServerTransaction nonInviteServerTransaction) {
super(id, nonInviteServerTransaction);
}
@Override
public void received1xx() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.PROCEEDING;
nonInviteServerTransaction.setState(nextState);
nonInviteServerTransaction.sendLastResponse();
}
@Override
public void received200To699() {
NonInviteServerTransactionState nextState =
nonInviteServerTransaction.COMPLETED;
nonInviteServerTransaction.setState(nextState);
}
}

View File

@@ -0,0 +1,35 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import java.util.Timer;
import peers.sip.transport.TransportManager;
public abstract class NonInviteTransaction extends Transaction {
protected NonInviteTransaction(String branchId, String method, Timer timer,
TransportManager transportManager,
TransactionManager transactionManager) {
super(branchId, method, timer, transportManager, transactionManager);
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
public interface ServerTransaction {
public void start();
public void receivedRequest(SipRequest sipRequest);
public void sendReponse(SipResponse sipResponse);
}

View File

@@ -0,0 +1,25 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public interface ServerTransactionUser {
public void transactionFailure();
}

View File

@@ -0,0 +1,64 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transaction;
public class SipListeningPoint {
private int localPort;
private String localTransport;
public SipListeningPoint(int localPort, String localTransport) {
super();
this.localPort = localPort;
this.localTransport = localTransport;
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() != SipListeningPoint.class) {
return false;
}
SipListeningPoint other = (SipListeningPoint)obj;
return localPort == other.localPort &&
localTransport.equals(other.localTransport);
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(':').append(localPort).append('/').append(localTransport);
return buf.toString();
}
@Override
public int hashCode() {
return toString().hashCode();
}
public int getlocalPort() {
return localPort;
}
public String getlocalTransport() {
return localTransport;
}
}

View File

@@ -0,0 +1,77 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transaction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public abstract class Transaction {
public static final char ID_SEPARATOR = '|';
protected String branchId;
protected String method;
protected SipRequest request;
protected List<SipResponse> responses;
protected Timer timer;
protected TransportManager transportManager;
protected TransactionManager transactionManager;
protected Transaction(String branchId, String method, Timer timer,
TransportManager transportManager,
TransactionManager transactionManager) {
this.branchId = branchId;
this.method = method;
this.timer = timer;
this.transportManager = transportManager;
this.transactionManager = transactionManager;
responses = Collections.synchronizedList(new ArrayList<SipResponse>());
}
protected String getId() {
StringBuffer buf = new StringBuffer();
buf.append(branchId).append(ID_SEPARATOR);
buf.append(method);
return buf.toString();
}
public SipResponse getLastResponse() {
if (responses.isEmpty()) {
return null;
}
return responses.get(responses.size() - 1);
}
public SipRequest getRequest() {
return request;
}
}

View File

@@ -0,0 +1,209 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transaction;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Timer;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;
public class TransactionManager {
protected Timer timer;
// TODO remove client transactions when they reach terminated state
// TODO check that server transactions are removed in all transitions to terminated
private Hashtable<String, ClientTransaction> clientTransactions;
private Hashtable<String, ServerTransaction> serverTransactions;
private TransportManager transportManager;
public TransactionManager() {
clientTransactions = new Hashtable<String, ClientTransaction>();
serverTransactions = new Hashtable<String, ServerTransaction>();
timer = new Timer(TransactionManager.class.getSimpleName()
+ " " + Timer.class.getSimpleName());
}
public ClientTransaction createClientTransaction(SipRequest sipRequest,
InetAddress inetAddress, int port, String transport,
String pBranchId, ClientTransactionUser clientTransactionUser) {
String branchId;
if (pBranchId == null || "".equals(pBranchId.trim())
|| !pBranchId.startsWith(RFC3261.BRANCHID_MAGIC_COOKIE)) {
branchId = Utils.generateBranchId();
} else {
branchId = pBranchId;
}
String method = sipRequest.getMethod();
ClientTransaction clientTransaction;
if (RFC3261.METHOD_INVITE.equals(method)) {
clientTransaction = new InviteClientTransaction(branchId,
inetAddress, port, transport, sipRequest, clientTransactionUser,
timer, transportManager, this);
} else {
clientTransaction = new NonInviteClientTransaction(branchId,
inetAddress, port, transport, sipRequest, clientTransactionUser,
timer, transportManager, this);
}
clientTransactions.put(getTransactionId(branchId, method),
clientTransaction);
return clientTransaction;
}
public ServerTransaction createServerTransaction(SipResponse sipResponse,
int port, String transport,
ServerTransactionUser serverTransactionUser,
SipRequest sipRequest) {
SipHeaderFieldValue via = Utils.getTopVia(sipResponse);
String branchId = via.getParam(new SipHeaderParamName(
RFC3261.PARAM_BRANCH));
String cseq = sipResponse.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).toString();
String method = cseq.substring(cseq.lastIndexOf(' ') + 1);
ServerTransaction serverTransaction;
// TODO create server transport user and pass it to server transaction
if (RFC3261.METHOD_INVITE.equals(method)) {
serverTransaction = new InviteServerTransaction(branchId, port,
transport, sipResponse, serverTransactionUser, sipRequest,
timer, this, transportManager);
// serverTransaction = new InviteServerTransaction(branchId);
} else {
serverTransaction = new NonInviteServerTransaction(branchId, port,
transport, method, serverTransactionUser, sipRequest, timer,
transportManager, this);
}
serverTransactions.put(getTransactionId(branchId, method),
serverTransaction);
return serverTransaction;
}
public ClientTransaction getClientTransaction(SipMessage sipMessage) {
SipHeaderFieldValue via = Utils.getTopVia(sipMessage);
String branchId = via.getParam(new SipHeaderParamName(
RFC3261.PARAM_BRANCH));
String cseq = sipMessage.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).toString();
String method = cseq.substring(cseq.lastIndexOf(' ') + 1);
return clientTransactions.get(getTransactionId(branchId, method));
}
public List<ClientTransaction> getClientTransactionsFromCallId(String callId,
String method) {
ArrayList<ClientTransaction> clientTransactionsFromCallId =
new ArrayList<ClientTransaction>();
for (ClientTransaction clientTransaction: clientTransactions.values()) {
Transaction transaction = (Transaction)clientTransaction;
SipRequest sipRequest = transaction.getRequest();
String reqCallId = Utils.getMessageCallId(sipRequest);
String reqMethod = sipRequest.getMethod();
if (callId.equals(reqCallId) && method.equals(reqMethod)) {
clientTransactionsFromCallId.add(clientTransaction);
}
}
return clientTransactionsFromCallId;
}
public ServerTransaction getServerTransaction(SipMessage sipMessage) {
SipHeaderFieldValue via = Utils.getTopVia(sipMessage);
String branchId = via.getParam(new SipHeaderParamName(
RFC3261.PARAM_BRANCH));
String method;
if (sipMessage instanceof SipRequest) {
method = ((SipRequest)sipMessage).getMethod();
} else {
String cseq = sipMessage.getSipHeaders().get(
new SipHeaderFieldName(RFC3261.HDR_CSEQ)).toString();
method = cseq.substring(cseq.lastIndexOf(' ') + 1);
}
if (RFC3261.METHOD_ACK.equals(method)) {
method = RFC3261.METHOD_INVITE;
// InviteServerTransaction inviteServerTransaction =
// (InviteServerTransaction)
// serverTransactions.get(getTransactionId(branchId, method));
// if (inviteServerTransaction == null) {
// Logger.debug("received ACK for unknown transaction" +
// " branchId = " + branchId + ", method = " + method);
// } else {
// SipResponse sipResponse =
// inviteServerTransaction.getLastResponse();
// if (sipResponse == null) {
// Logger.debug("received ACK but no response sent " +
// "branchId = " + branchId + ", method = " + method);
// } else {
// int statusCode = sipResponse.getStatusCode();
// if (statusCode >= RFC3261.CODE_MIN_SUCCESS &&
// statusCode < RFC3261.CODE_MIN_REDIR) {
// // success response => ACK is not in INVITE server
// // transaction
// return null;
// } else {
// // error => ACK belongs to INVITE server transaction
// return inviteServerTransaction;
// }
// }
// }
// TODO if positive response, ACK does not belong to transaction
// retrieve transaction and take responses from transaction
// and check if a positive response has been received
// if it is the case, a new standalone transaction must be created
// for the ACK
}
return serverTransactions.get(getTransactionId(branchId, method));
}
public ServerTransaction getServerTransaction(String branchId, String method) {
return serverTransactions.get(getTransactionId(branchId, method));
}
void removeServerTransaction(String branchId, String method) {
serverTransactions.remove(getTransactionId(branchId, method));
}
void removeClientTransaction(String branchId, String method) {
clientTransactions.remove(getTransactionId(branchId, method));
}
private String getTransactionId(String branchId, String method) {
StringBuffer buf = new StringBuffer();
buf.append(branchId);
buf.append(Transaction.ID_SEPARATOR);
buf.append(method);
return buf.toString();
}
public void setTransportManager(TransportManager transportManager) {
this.transportManager = transportManager;
}
}

View File

@@ -0,0 +1,259 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import java.util.ArrayList;
import org.pmw.tinylog.Logger;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.NameAddress;
import peers.sip.syntaxencoding.SipHeaderFieldMultiValue;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transport.SipRequest;
public class Dialog {
public static final char ID_SEPARATOR = '|';
public static final int EMPTY_CSEQ = -1;
public final DialogState INIT;
public final DialogState EARLY;
public final DialogState CONFIRMED;
public final DialogState TERMINATED;
private DialogState state;
private String callId;
private String localTag;
private String remoteTag;
private int localCSeq;
private int remoteCSeq;
private String localUri;
private String remoteUri;
private String remoteTarget;
private boolean secure;
private ArrayList<String> routeSet;
Dialog(String callId, String localTag, String remoteTag) {
super();
this.callId = callId;
this.localTag = localTag;
this.remoteTag = remoteTag;
INIT = new DialogStateInit(getId(), this);
state = INIT;
EARLY = new DialogStateEarly(getId(), this);
CONFIRMED = new DialogStateConfirmed(getId(), this);
TERMINATED = new DialogStateTerminated(getId(), this);
localCSeq = EMPTY_CSEQ;
remoteCSeq = EMPTY_CSEQ;
}
public void receivedOrSent1xx() {
state.receivedOrSent101To199();
}
public void receivedOrSent2xx() {
state.receivedOrSent2xx();
}
public void receivedOrSent300To699() {
state.receivedOrSent300To699();
}
public void receivedOrSentBye() {
state.receivedOrSentBye();
}
public void setState(DialogState state) {
this.state.log(state);
this.state = state;
}
public SipRequest buildSubsequentRequest(String method) {
//12.2.1.1
SipURI sipUri;
try {
sipUri = new SipURI(remoteTarget);
} catch (SipUriSyntaxException e) {
throw new RuntimeException(e);
//TODO check remote target when message is received
}
SipRequest subsequentRequest = new SipRequest(method, sipUri);
SipHeaders headers = subsequentRequest.getSipHeaders();
//To
SipHeaderFieldValue to = new SipHeaderFieldValue(
new NameAddress(remoteUri).toString());
if (remoteTag != null) {
to.addParam(new SipHeaderParamName(RFC3261.PARAM_TAG), remoteTag);
}
headers.add(new SipHeaderFieldName(RFC3261.HDR_TO), to);
//From
SipHeaderFieldValue from = new SipHeaderFieldValue(
new NameAddress(localUri).toString());
if (localTag != null) {
from.addParam(new SipHeaderParamName(RFC3261.PARAM_TAG), localTag);
}
headers.add(new SipHeaderFieldName(RFC3261.HDR_FROM), from);
//Call-ID
SipHeaderFieldValue callIdValue = new SipHeaderFieldValue(callId);
headers.add(new SipHeaderFieldName(RFC3261.HDR_CALLID), callIdValue);
//CSeq
if (localCSeq == Dialog.EMPTY_CSEQ) {
localCSeq = ((int)(System.currentTimeMillis() / 1000) & 0xFFFFFFFE) >> 1;
} else {
localCSeq++;
}
headers.add(new SipHeaderFieldName(RFC3261.HDR_CSEQ),
new SipHeaderFieldValue(localCSeq + " " + method));
//Route
if (!routeSet.isEmpty()) {
if (routeSet.get(0).contains(RFC3261.LOOSE_ROUTING)) {
ArrayList<SipHeaderFieldValue> routes = new ArrayList<SipHeaderFieldValue>();
for (String route : routeSet) {
routes.add(new SipHeaderFieldValue(route));
}
headers.add(new SipHeaderFieldName(RFC3261.HDR_ROUTE),
new SipHeaderFieldMultiValue(routes));
} else {
Logger.error("Trying to forward to a strict router, forbidden in this implementation");
}
}
Utils.addCommonHeaders(headers);
return subsequentRequest;
}
public String getId() {
StringBuffer buf = new StringBuffer();
buf.append(callId).append(ID_SEPARATOR);
buf.append(localTag).append(ID_SEPARATOR);
buf.append(remoteTag);
return buf.toString();
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public int getLocalCSeq() {
return localCSeq;
}
public void setLocalCSeq(int localCSeq) {
this.localCSeq = localCSeq;
}
public String getLocalUri() {
return localUri;
}
public void setLocalUri(String localUri) {
this.localUri = localUri;
}
public int getRemoteCSeq() {
return remoteCSeq;
}
public void setRemoteCSeq(int remoteCSeq) {
this.remoteCSeq = remoteCSeq;
}
public String getRemoteTarget() {
return remoteTarget;
}
public void setRemoteTarget(String remoteTarget) {
this.remoteTarget = remoteTarget;
}
public String getRemoteUri() {
return remoteUri;
}
public void setRemoteUri(String remoteUri) {
this.remoteUri = remoteUri;
}
public ArrayList<String> getRouteSet() {
return routeSet;
}
public void setRouteSet(ArrayList<String> routeSet) {
this.routeSet = routeSet;
}
public boolean isSecure() {
return secure;
}
public void setSecure(boolean secure) {
this.secure = secure;
}
public String getLocalTag() {
return localTag;
}
public void setLocalTag(String localTag) {
this.localTag = localTag;
}
public String getRemoteTag() {
return remoteTag;
}
public void setRemoteTag(String remoteTag) {
this.remoteTag = remoteTag;
}
public DialogState getState() {
return state;
}
}

View File

@@ -0,0 +1,115 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import java.util.Collection;
import java.util.Hashtable;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipResponse;
public class DialogManager {
private Hashtable<String, Dialog> dialogs;
public DialogManager() {
dialogs = new Hashtable<String, Dialog>();
}
/**
* @param sipResponse sip response must contain a To tag, a
* From tag and a Call-ID
* @return the new Dialog created
*/
public synchronized Dialog createDialog(SipResponse sipResponse) {
SipHeaders sipHeaders = sipResponse.getSipHeaders();
String callID = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CALLID)).toString();
SipHeaderFieldValue from = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_FROM));
SipHeaderFieldValue to = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_TO));
String fromTag = from.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
String toTag = to.getParam(new SipHeaderParamName(RFC3261.PARAM_TAG));
Dialog dialog;
if (sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_VIA)) == null) {
//createDialog is called from UAS side, in layer Transaction User
dialog = new Dialog(callID, toTag, fromTag);
} else {
//createDialog is called from UAC side, in syntax encoding layer
dialog = new Dialog(callID, fromTag, toTag);
}
dialogs.put(dialog.getId(), dialog);
return dialog;
}
public void removeDialog(String dialogId) {
dialogs.remove(dialogId);
}
public synchronized Dialog getDialog(SipMessage sipMessage) {
SipHeaders sipHeaders = sipMessage.getSipHeaders();
String callID = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_CALLID)).toString();
SipHeaderFieldValue from = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_FROM));
SipHeaderFieldValue to = sipHeaders.get(
new SipHeaderFieldName(RFC3261.HDR_TO));
SipHeaderParamName tagName = new SipHeaderParamName(RFC3261.PARAM_TAG);
String fromTag = from.getParam(tagName);
String toTag = to.getParam(tagName);
Dialog dialog = dialogs.get(getDialogId(callID, fromTag, toTag));
if (dialog != null) {
return dialog;
}
return dialogs.get(getDialogId(callID, toTag, fromTag));
}
public synchronized Dialog getDialog(String callId) {
for (Dialog dialog : dialogs.values()) {
if (dialog.getCallId().equals(callId)) {
return dialog;
}
}
return null;
}
private String getDialogId(String callID, String localTag, String remoteTag) {
StringBuffer buf = new StringBuffer();
buf.append(callID);
buf.append(Dialog.ID_SEPARATOR);
buf.append(localTag);
buf.append(Dialog.ID_SEPARATOR);
buf.append(remoteTag);
return buf.toString();
}
public Collection<Dialog> getDialogCollection() {
return dialogs.values();
}
}

View File

@@ -0,0 +1,40 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import peers.sip.AbstractState;
public abstract class DialogState extends AbstractState {
protected Dialog dialog;
public DialogState(String id, Dialog dialog) {
super(id);
this.dialog = dialog;
}
public void receivedOrSent101To199() {}
public void receivedOrSent2xx() {}
public void receivedOrSent300To699() {}
//sent or received a BYE for RFC3261
public void receivedOrSentBye() {}
}

View File

@@ -0,0 +1,55 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import org.pmw.tinylog.Logger;
public class DialogStateConfirmed extends DialogState {
public DialogStateConfirmed(String id, Dialog dialog) {
super(id, dialog);
}
@Override
public void receivedOrSent101To199() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSent2xx() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSent300To699() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSentBye() {
DialogState nextState = dialog.TERMINATED;
dialog.setState(nextState);
}
}

View File

@@ -0,0 +1,54 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import org.pmw.tinylog.Logger;
public class DialogStateEarly extends DialogState {
public DialogStateEarly(String id, Dialog dialog) {
super(id, dialog);
}
@Override
public void receivedOrSent101To199() {
DialogState nextState = dialog.EARLY;
dialog.setState(nextState);
}
@Override
public void receivedOrSent2xx() {
DialogState nextState = dialog.CONFIRMED;
dialog.setState(nextState);
}
@Override
public void receivedOrSent300To699() {
DialogState nextState = dialog.TERMINATED;
dialog.setState(nextState);
}
@Override
public void receivedOrSentBye() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,54 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transactionuser;
import org.pmw.tinylog.Logger;
public class DialogStateInit extends DialogState {
public DialogStateInit(String id, Dialog dialog) {
super(id, dialog);
}
@Override
public void receivedOrSent101To199() {
DialogState nextState = dialog.EARLY;
dialog.setState(nextState);
}
@Override
public void receivedOrSent2xx() {
DialogState nextState = dialog.CONFIRMED;
dialog.setState(nextState);
}
@Override
public void receivedOrSent300To699() {
DialogState nextState = dialog.TERMINATED;
dialog.setState(nextState);
}
@Override
public void receivedOrSentBye() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,55 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transactionuser;
import org.pmw.tinylog.Logger;
public class DialogStateTerminated extends DialogState {
public DialogStateTerminated(String id, Dialog dialog) {
super(id, dialog);
}
@Override
public void receivedOrSent101To199() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSent2xx() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSent300To699() {
Logger.error(id + " invalid transition");
throw new IllegalStateException();
}
@Override
public void receivedOrSentBye() {
//ignore bye retransmissions
// Logger.error(id + " invalid transition");
// throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,205 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipParserException;
import peers.sip.transaction.ClientTransaction;
import peers.sip.transaction.ServerTransaction;
import peers.sip.transaction.TransactionManager;
public abstract class MessageReceiver implements Runnable {
public static final int BUFFER_SIZE = 2048;//FIXME should correspond to MTU 1024;
public static final String CHARACTER_ENCODING = "US-ASCII";
protected int port;
private boolean isListening;
//private UAS uas;
private SipServerTransportUser sipServerTransportUser;
private TransactionManager transactionManager;
private TransportManager transportManager;
private Config config;
public MessageReceiver(int port, TransactionManager transactionManager,
TransportManager transportManager, Config config) {
super();
this.port = port;
this.transactionManager = transactionManager;
this.transportManager = transportManager;
this.config = config;
isListening = true;
}
public void run() {
while (isListening) {
try {
listen();
} catch (IOException e) {
Logger.error("input/output error", e);
}
}
}
protected abstract void listen() throws IOException;
protected boolean isRequest(byte[] message) {
String beginning = null;
try {
beginning = new String(message, 0,
RFC3261.DEFAULT_SIP_VERSION.length(), CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Logger.error("unsupported encoding", e);
}
if (RFC3261.DEFAULT_SIP_VERSION.equals(beginning)) {
return false;
}
return true;
}
protected void processMessage(byte[] message, InetAddress sourceIp,
int sourcePort, String transport) throws IOException {
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(message);
InputStreamReader inputStreamReader = new InputStreamReader(
byteArrayInputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);
String startLine = reader.readLine();
while ("".equals(startLine)) {
startLine = reader.readLine();
}
if (startLine == null) {
return;
}
if (!startLine.contains(RFC3261.DEFAULT_SIP_VERSION)) {
// keep-alive, send back to sender
SipTransportConnection sipTransportConnection =
new SipTransportConnection(config.getLocalInetAddress(),
port, sourceIp, sourcePort, transport);
MessageSender messageSender = transportManager.getMessageSender(
sipTransportConnection);
if (messageSender != null) {
messageSender.sendBytes(message);
}
return;
}
StringBuffer direction = new StringBuffer();
direction.append("RECEIVED from ").append(sourceIp.getHostAddress());
direction.append("/").append(sourcePort);
Logger.info(new String(message),direction.toString());
SipMessage sipMessage = null;
try {
sipMessage = transportManager.sipParser.parse(
new ByteArrayInputStream(message));
} catch (IOException e) {
Logger.error("input/output error", e);
} catch (SipParserException e) {
Logger.error("SIP parser error", e);
}
if (sipMessage == null) {
return;
}
// RFC3261 18.2
if (sipMessage instanceof SipRequest) {
SipRequest sipRequest = (SipRequest)sipMessage;
SipHeaderFieldValue topVia = Utils.getTopVia(sipRequest);
String sentBy =
topVia.getParam(new SipHeaderParamName(RFC3261.PARAM_SENTBY));
if (sentBy != null) {
int colonPos = sentBy.indexOf(RFC3261.TRANSPORT_PORT_SEP);
if (colonPos < 0) {
colonPos = sentBy.length();
}
sentBy = sentBy.substring(0, colonPos);
if (!InetAddress.getByName(sentBy).equals(sourceIp)) {
topVia.addParam(new SipHeaderParamName(
RFC3261.PARAM_RECEIVED),
sourceIp.getHostAddress());
}
}
//RFC3581
//TODO check rport configuration
SipHeaderParamName rportName = new SipHeaderParamName(
RFC3261.PARAM_RPORT);
String rport = topVia.getParam(rportName);
if (rport != null && "".equals(rport)) {
topVia.removeParam(rportName);
topVia.addParam(rportName, String.valueOf(sourcePort));
}
ServerTransaction serverTransaction =
transactionManager.getServerTransaction(sipRequest);
if (serverTransaction == null) {
//uas.messageReceived(sipMessage);
sipServerTransportUser.messageReceived(sipMessage);
} else {
serverTransaction.receivedRequest(sipRequest);
}
} else {
SipResponse sipResponse = (SipResponse)sipMessage;
ClientTransaction clientTransaction =
transactionManager.getClientTransaction(sipResponse);
Logger.debug("ClientTransaction = " + clientTransaction);
if (clientTransaction == null) {
//uas.messageReceived(sipMessage);
sipServerTransportUser.messageReceived(sipMessage);
} else {
clientTransaction.receivedResponse(sipResponse);
}
}
}
public synchronized void setListening(boolean isListening) {
this.isListening = isListening;
}
public synchronized boolean isListening() {
return isListening;
}
public void setSipServerTransportUser(
SipServerTransportUser sipServerTransportUser) {
this.sipServerTransportUser = sipServerTransportUser;
}
// public void setUas(UAS uas) {
// this.uas = uas;
// }
}

View File

@@ -0,0 +1,102 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transport;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Timer;
import java.util.TimerTask;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
public abstract class MessageSender {
public static final int KEEY_ALIVE_INTERVAL = 25; // seconds
protected InetAddress inetAddress;
protected int port;
protected int localPort;
private Config config;
private String transportName;
private Timer timer;
public MessageSender(int localPort, InetAddress inetAddress,
int port, Config config,
String transportName) {
super();
this.localPort = localPort;
this.inetAddress = inetAddress;
this.port = port;
this.config = config;
this.transportName = transportName;
timer = new Timer(getClass().getSimpleName() + " "
+ Timer.class.getSimpleName());
//TODO check config
timer.scheduleAtFixedRate(new KeepAlive(), 0,
1000 * KEEY_ALIVE_INTERVAL);
}
public abstract void sendMessage(SipMessage sipMessage) throws IOException;
public abstract void sendBytes(byte[] bytes) throws IOException;
public String getContact() {
StringBuffer buf = new StringBuffer();
InetAddress myAddress = config.getPublicInetAddress();
if (myAddress == null) {
myAddress = config.getLocalInetAddress();
}
buf.append(myAddress.getHostAddress());
buf.append(RFC3261.TRANSPORT_PORT_SEP);
//buf.append(config.getSipPort());
buf.append(localPort);
buf.append(RFC3261.PARAM_SEPARATOR);
buf.append(RFC3261.PARAM_TRANSPORT);
buf.append(RFC3261.PARAM_ASSIGNMENT);
buf.append(transportName);
return buf.toString();
}
public int getLocalPort() {
return localPort;
}
public void stopKeepAlives() {
timer.cancel();
}
class KeepAlive extends TimerTask {
@Override
public void run() {
byte[] bytes = (RFC3261.CRLF + RFC3261.CRLF).getBytes();
try {
sendBytes(bytes);
} catch (IOException e) {
Logger.error(e.getMessage(), e);
}
}
}
}

View File

@@ -0,0 +1,27 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
public interface SipClientTransportUser {
public void requestTransportError(SipRequest sipRequest, Exception e);
public void responseTransportError(Exception e);
}

View File

@@ -0,0 +1,80 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaders;
public abstract class SipMessage {
protected String sipVersion;
protected SipHeaders sipHeaders;
protected byte[] body;
public SipMessage() {
sipVersion = RFC3261.DEFAULT_SIP_VERSION;
sipHeaders = new SipHeaders();
}
public String getSipVersion() {
return sipVersion;
}
public void setSipHeaders(SipHeaders sipHeaders) {
this.sipHeaders = sipHeaders;
}
public SipHeaders getSipHeaders() {
return sipHeaders;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
SipHeaderFieldName contentLengthName =
new SipHeaderFieldName(RFC3261.HDR_CONTENT_LENGTH);
SipHeaderFieldValue contentLengthValue =
sipHeaders.get(contentLengthName);
if (contentLengthValue == null) {
contentLengthValue = new SipHeaderFieldValue(
String.valueOf(body.length));
sipHeaders.add(contentLengthName, contentLengthValue);
} else {
contentLengthValue.setValue(String.valueOf(body.length));
}
this.body = body;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(sipHeaders.toString());
buf.append(RFC3261.CRLF);
if (body != null) {
buf.append(new String(body));
}
return buf.toString();
}
}

View File

@@ -0,0 +1,53 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import peers.sip.RFC3261;
import peers.sip.syntaxencoding.SipURI;
public class SipRequest extends SipMessage {
protected String method;
protected SipURI requestUri;
//protected String requestUri;
public SipRequest(String method, SipURI requestUri) {
super();
this.method = method;
this.requestUri = requestUri;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(method).append(' ').append(requestUri).append(
' ').append(RFC3261.DEFAULT_SIP_VERSION).append(RFC3261.CRLF);
buf.append(super.toString());
return buf.toString();
}
public String getMethod() {
return method;
}
public SipURI getRequestUri() {
return requestUri;
}
}

View File

@@ -0,0 +1,50 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import peers.sip.RFC3261;
public class SipResponse extends SipMessage {
protected int statusCode;
protected String reasonPhrase;
public SipResponse(int statusCode, String reasonPhrase) {
this.statusCode = statusCode;
this.reasonPhrase = reasonPhrase;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(RFC3261.DEFAULT_SIP_VERSION).append(' ').append(statusCode
).append(' ').append(reasonPhrase).append(RFC3261.CRLF);
buf.append(super.toString());
return buf.toString();
}
public int getStatusCode() {
return statusCode;
}
public String getReasonPhrase() {
return reasonPhrase;
}
}

View File

@@ -0,0 +1,25 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
public interface SipServerTransportUser {
public void messageReceived(SipMessage sipMessage);
}

View File

@@ -0,0 +1,123 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transport;
import java.net.InetAddress;
import peers.sip.RFC3261;
public class SipTransportConnection {
public static final int EMPTY_PORT = -1;
private InetAddress localInetAddress;
private int localPort = EMPTY_PORT;
private InetAddress remoteInetAddress;
private int remotePort = EMPTY_PORT;
private String transport;// UDP, TCP or SCTP
public SipTransportConnection(InetAddress localInetAddress,
int localPort, InetAddress remoteInetAddress, int remotePort,
String transport) {
this.localInetAddress = localInetAddress;
this.localPort = localPort;
this.remoteInetAddress = remoteInetAddress;
this.remotePort = remotePort;
this.transport = transport;
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() != SipTransportConnection.class) {
return false;
}
SipTransportConnection other = (SipTransportConnection)obj;
if (!transport.equalsIgnoreCase(other.transport)) {
return false;
}
if (RFC3261.TRANSPORT_UDP.equalsIgnoreCase(transport)) {
return localInetAddress.equals(other.localInetAddress) &&
localPort == other.localPort;
}
return false;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
appendInetAddress(buf, localInetAddress);
buf.append(':');
appendPort(buf, localPort);
buf.append('/');
if (!RFC3261.TRANSPORT_UDP.equalsIgnoreCase(transport)) {
appendInetAddress(buf, remoteInetAddress);
buf.append(':');
appendPort(buf, remotePort);
buf.append('/');
}
buf.append(transport.toUpperCase());
return buf.toString();
}
private void appendInetAddress(StringBuffer buf, InetAddress inetAddress) {
if (inetAddress != null) {
buf.append(inetAddress.getHostAddress());
} else {
buf.append("-");
}
}
private void appendPort(StringBuffer buf, int port) {
if (port != EMPTY_PORT) {
buf.append(port);
} else {
buf.append("-");
}
}
@Override
public int hashCode() {
return toString().hashCode();
}
public InetAddress getLocalInetAddress() {
return localInetAddress;
}
public int getLocalPort() {
return localPort;
}
public InetAddress getRemoteInetAddress() {
return remoteInetAddress;
}
public int getRemotePort() {
return remotePort;
}
public String getTransport() {
return transport;
}
}

View File

@@ -0,0 +1,479 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007-2013 Yohann Martineau
*/
package peers.sip.transport;
import static peers.sip.RFC3261.DEFAULT_SIP_VERSION;
import static peers.sip.RFC3261.IPV4_TTL;
import static peers.sip.RFC3261.PARAM_MADDR;
import static peers.sip.RFC3261.PARAM_TTL;
import static peers.sip.RFC3261.TRANSPORT_DEFAULT_PORT;
import static peers.sip.RFC3261.TRANSPORT_PORT_SEP;
import static peers.sip.RFC3261.TRANSPORT_SCTP;
import static peers.sip.RFC3261.TRANSPORT_TCP;
import static peers.sip.RFC3261.TRANSPORT_TLS_PORT;
import static peers.sip.RFC3261.TRANSPORT_UDP;
import static peers.sip.RFC3261.TRANSPORT_UDP_USUAL_MAX_SIZE;
import static peers.sip.RFC3261.TRANSPORT_VIA_SEP;
import static peers.sip.RFC3261.TRANSPORT_VIA_SEP2;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Hashtable;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
import peers.sip.Utils;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaderParamName;
import peers.sip.syntaxencoding.SipHeaders;
import peers.sip.syntaxencoding.SipParser;
import peers.sip.transaction.TransactionManager;
public class TransportManager {
public static final int SOCKET_TIMEOUT = RFC3261.TIMER_T1;
private static int NO_TTL = -1;
//private UAS uas;
private SipServerTransportUser sipServerTransportUser;
protected SipParser sipParser;
private Hashtable<SipTransportConnection, DatagramSocket> datagramSockets;
private Hashtable<SipTransportConnection, MessageSender> messageSenders;
private Hashtable<SipTransportConnection, MessageReceiver> messageReceivers;
private TransactionManager transactionManager;
private Config config;
private int sipPort;
public TransportManager(TransactionManager transactionManager,
Config config ) {
sipParser = new SipParser();
datagramSockets = new Hashtable<SipTransportConnection, DatagramSocket>();
messageSenders = new Hashtable<SipTransportConnection, MessageSender>();
messageReceivers = new Hashtable<SipTransportConnection, MessageReceiver>();
this.transactionManager = transactionManager;
this.config = config;
}
public MessageSender createClientTransport(SipRequest sipRequest,
InetAddress inetAddress, int port, String transport)
throws IOException {
return createClientTransport(sipRequest, inetAddress, port, transport,
NO_TTL);
}
public MessageSender createClientTransport(SipRequest sipRequest,
InetAddress inetAddress, int port, String transport, int ttl)
throws IOException {
//18.1
//via created by transaction layer to add branchid
SipHeaderFieldValue via = Utils.getTopVia(sipRequest);
StringBuffer buf = new StringBuffer(DEFAULT_SIP_VERSION);
buf.append(TRANSPORT_VIA_SEP);
if (sipRequest.toString().getBytes().length > TRANSPORT_UDP_USUAL_MAX_SIZE) {
transport = TRANSPORT_TCP;
}
buf.append(transport);
if (inetAddress.isMulticastAddress()) {
SipHeaderParamName maddrName = new SipHeaderParamName(PARAM_MADDR);
via.addParam(maddrName, inetAddress.getHostAddress());
if (inetAddress instanceof Inet4Address) {
SipHeaderParamName ttlName = new SipHeaderParamName(PARAM_TTL);
via.addParam(ttlName, IPV4_TTL);
}
}
//RFC3581
//TODO check config
via.addParam(new SipHeaderParamName(RFC3261.PARAM_RPORT), "");
buf.append(TRANSPORT_VIA_SEP2);//space
//TODO user server connection
InetAddress myAddress = config.getPublicInetAddress();
if (myAddress == null) {
myAddress = config.getLocalInetAddress();
}
buf.append(myAddress.getHostAddress()); //TODO use getHostName if real DNS
buf.append(TRANSPORT_PORT_SEP);
if (sipPort < 1) {
//use default port
if (TRANSPORT_TCP.equals(transport) || TRANSPORT_UDP.equals(transport)
|| TRANSPORT_SCTP.equals(transport)) {
sipPort = TRANSPORT_DEFAULT_PORT;
} else if (TRANSPORT_SCTP.equals(transport)) {
sipPort = TRANSPORT_TLS_PORT;
} else {
throw new RuntimeException("unknown transport type");
}
}
buf.append(sipPort);
//TODO add sent-by (p. 143) Before...
via.setValue(buf.toString());
SipTransportConnection connection = new SipTransportConnection(
config.getLocalInetAddress(), sipPort, inetAddress, port,
transport);
MessageSender messageSender = messageSenders.get(connection);
if (messageSender == null) {
messageSender = createMessageSender(connection);
}
return messageSender;
}
private String threadName(int port) {
return getClass().getSimpleName() + " " + port;
}
public void createServerTransport(String transportType, int port)
throws SocketException {
SipTransportConnection conn = new SipTransportConnection(
config.getLocalInetAddress(), port, null,
SipTransportConnection.EMPTY_PORT, transportType);
MessageReceiver messageReceiver = messageReceivers.get(conn);
if (messageReceiver == null) {
messageReceiver = createMessageReceiver(conn);
new Thread(messageReceiver, threadName(port)).start();
}
if (!messageReceiver.isListening()) {
new Thread(messageReceiver, threadName(port)).start();
}
}
public void sendResponse(SipResponse sipResponse) throws IOException {
//18.2.2
SipHeaderFieldValue topVia = Utils.getTopVia(sipResponse);
String topViaValue = topVia.getValue();
StringBuffer buf = new StringBuffer(topViaValue);
String hostport = null;
int i = topViaValue.length() - 1;
while (i > 0) {
char c = buf.charAt(i);
if (c == ' ' || c == '\t') {
hostport = buf.substring(i + 1);
break;
}
--i;
}
if (hostport == null) {
throw new RuntimeException("host or ip address not found in top via");
}
String host;
int port;
int colonPos = hostport.indexOf(RFC3261.TRANSPORT_PORT_SEP);
if (colonPos > -1) {
host = hostport.substring(0, colonPos);
port = Integer.parseInt(
hostport.substring(colonPos + 1, hostport.length()));
} else {
host = hostport;
port = RFC3261.TRANSPORT_DEFAULT_PORT;
}
String transport;
if (buf.indexOf(RFC3261.TRANSPORT_TCP) > -1) {
transport = RFC3261.TRANSPORT_TCP;
} else if (buf.indexOf(RFC3261.TRANSPORT_UDP) > -1) {
transport = RFC3261.TRANSPORT_UDP;
} else {
Logger.error("no transport found in top via header, discarding response");
return;
}
String received =
topVia.getParam(new SipHeaderParamName(RFC3261.PARAM_RECEIVED));
if (received != null) {
host = received;
}
//RFC3581
//TODO check config
String rport = topVia.getParam(new SipHeaderParamName(
RFC3261.PARAM_RPORT));
if (rport != null && !"".equals(rport.trim())) {
port = Integer.parseInt(rport);
}
SipTransportConnection connection;
try {
connection = new SipTransportConnection(config.getLocalInetAddress(),
sipPort, InetAddress.getByName(host),
port, transport);
} catch (UnknownHostException e) {
Logger.error("unknwon host {}", e);
return;
}
//actual sending
//TODO manage maddr parameter in top via for multicast
if (buf.indexOf(RFC3261.TRANSPORT_TCP) > -1) {
// Socket socket = (Socket)factory.connections.get(connection);
// if (!socket.isClosed()) {
// try {
// socket.getOutputStream().write(data);
// } catch (IOException e) {
// e.printStackTrace();
// return;
// //TODO
// }
// } else {
// try {
// socket = new Socket(host, port);
// factory.connections.put(connection, socket);
// socket.getOutputStream().write(data);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// /*
// * TODO
// * If connection attempt fails, use the procedures in RFC3263
// * for servers in order to determine the IP address and
// * port to open the connection and send the response to.
// */
// return;
// }
// }
} else {
MessageSender messageSender = messageSenders.get(connection);
if (messageSender == null) {
messageSender = createMessageSender(connection);
}
//add contact header
SipHeaderFieldName contactName = new SipHeaderFieldName(RFC3261.HDR_CONTACT);
SipHeaders respHeaders = sipResponse.getSipHeaders();
StringBuffer contactBuf = new StringBuffer();
contactBuf.append(RFC3261.LEFT_ANGLE_BRACKET);
contactBuf.append(RFC3261.SIP_SCHEME);
contactBuf.append(RFC3261.SCHEME_SEPARATOR);
contactBuf.append(messageSender.getContact());
contactBuf.append(RFC3261.RIGHT_ANGLE_BRACKET);
respHeaders.add(contactName, new SipHeaderFieldValue(contactBuf.toString()));
messageSender.sendMessage(sipResponse);
}
}
private MessageSender createMessageSender(final SipTransportConnection conn)
throws IOException {
MessageSender messageSender = null;
Object socket = null;
if (RFC3261.TRANSPORT_UDP.equalsIgnoreCase(conn.getTransport())) {
//TODO use Utils.getMyAddress to create socket on appropriate NIC
DatagramSocket datagramSocket = datagramSockets.get(conn);
if (datagramSocket == null) {
Logger.debug("new DatagramSocket(" + conn.getLocalPort()
+ ", " + conn.getLocalInetAddress());
// AccessController.doPrivileged added for plugin compatibility
datagramSocket = AccessController.doPrivileged(
new PrivilegedAction<DatagramSocket>() {
@Override
public DatagramSocket run() {
try {
return new DatagramSocket(conn.getLocalPort(),
conn.getLocalInetAddress());
} catch (SocketException e) {
Logger.error("cannot create socket", e);
} catch (SecurityException e) {
Logger.error("security exception", e);
}
return null;
}
}
);
if (datagramSocket == null) {
throw new SocketException();
}
datagramSocket.setSoTimeout(SOCKET_TIMEOUT);
datagramSockets.put(conn, datagramSocket);
Logger.info("added datagram socket " + conn);
}
socket = datagramSocket;
messageSender = new UdpMessageSender(conn.getRemoteInetAddress(),
conn.getRemotePort(), datagramSocket, config);
} else {
// TODO
// messageReceiver = new TcpMessageReceiver(port);
}
messageSenders.put(conn, messageSender);
//when a mesage is sent over a transport, the transport layer
//must also be able to receive messages on this transport
// MessageReceiver messageReceiver =
// createMessageReceiver(conn, socket);
MessageReceiver messageReceiver = messageReceivers.get(conn);
if (messageReceiver == null) {
messageReceiver = createMessageReceiver(conn, socket);
new Thread(messageReceiver, threadName(conn.getLocalPort())).start();
}
// if (RFC3261.TRANSPORT_UDP.equalsIgnoreCase(conn.getTransport())) {
// messageSender = new UdpMessageSender(conn.getRemoteInetAddress(),
// conn.getRemotePort(), (DatagramSocket)socket, config);
// messageSenders.put(conn, messageSender);
// }
return messageSender;
}
private MessageReceiver createMessageReceiver(SipTransportConnection conn,
Object socket) throws IOException {
MessageReceiver messageReceiver = null;
if (RFC3261.TRANSPORT_UDP.equalsIgnoreCase(conn.getTransport())) {
DatagramSocket datagramSocket = (DatagramSocket)socket;
messageReceiver = new UdpMessageReceiver(datagramSocket,
transactionManager, this, config);
messageReceiver.setSipServerTransportUser(sipServerTransportUser);
}
messageReceivers.put(conn, messageReceiver);
return messageReceiver;
}
private MessageReceiver createMessageReceiver(final SipTransportConnection conn)
throws SocketException {
MessageReceiver messageReceiver = null;
SipTransportConnection sipTransportConnection = conn;
if (RFC3261.TRANSPORT_UDP.equals(conn.getTransport())) {
DatagramSocket datagramSocket = datagramSockets.get(conn);
if (datagramSocket == null) {
Logger.debug("new DatagramSocket(" + conn.getLocalPort()
+ ", " + conn.getLocalInetAddress());
// AccessController.doPrivileged added for plugin compatibility
datagramSocket = AccessController.doPrivileged(
new PrivilegedAction<DatagramSocket>() {
@Override
public DatagramSocket run() {
try {
return new DatagramSocket(conn.getLocalPort(),
conn.getLocalInetAddress());
} catch (SocketException e) {
Logger.error("cannot create socket", e);
} catch (SecurityException e) {
Logger.error("security exception", e);
}
return null;
}
}
);
datagramSocket.setSoTimeout(SOCKET_TIMEOUT);
if (conn.getLocalPort() == 0) {
sipTransportConnection = new SipTransportConnection(
conn.getLocalInetAddress(),
datagramSocket.getLocalPort(),
conn.getRemoteInetAddress(),
conn.getRemotePort(),
conn.getTransport());
//config.setSipPort(datagramSocket.getLocalPort());
}
sipPort = datagramSocket.getLocalPort();
datagramSockets.put(sipTransportConnection, datagramSocket);
Logger.info("added datagram socket " + sipTransportConnection);
}
messageReceiver = new UdpMessageReceiver(datagramSocket,
transactionManager, this, config);
messageReceiver.setSipServerTransportUser(sipServerTransportUser);
//TODO create also tcp receiver using a recursive call
} else {
//TODO
//messageReceiver = new TcpMessageReceiver(port);
}
messageReceivers.put(sipTransportConnection, messageReceiver);
Logger.info("added " + sipTransportConnection + ": " + messageReceiver
+ " to message receivers");
return messageReceiver;
}
public void setSipServerTransportUser(
SipServerTransportUser sipServerTransportUser) {
this.sipServerTransportUser = sipServerTransportUser;
}
public void closeTransports() {
for (MessageReceiver messageReceiver: messageReceivers.values()) {
messageReceiver.setListening(false);
}
for (MessageSender messageSender: messageSenders.values()) {
messageSender.stopKeepAlives();
}
try
{
Thread.sleep(SOCKET_TIMEOUT);
}
catch (InterruptedException e)
{
return;
}
// AccessController.doPrivileged added for plugin compatibility
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
@Override
public Void run() {
for (DatagramSocket datagramSocket: datagramSockets.values()) {
datagramSocket.close();
}
return null;
}
}
);
datagramSockets.clear();
messageReceivers.clear();
messageSenders.clear();
}
public MessageSender getMessageSender(
SipTransportConnection sipTransportConnection) {
return messageSenders.get(sipTransportConnection);
}
public int getSipPort() {
return sipPort;
}
public void setSipPort(int sipPort) {
this.sipPort = sipPort;
}
}

View File

@@ -0,0 +1,90 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
import peers.sip.transaction.TransactionManager;
public class UdpMessageReceiver extends MessageReceiver {
private DatagramSocket datagramSocket;
public UdpMessageReceiver(DatagramSocket datagramSocket,
TransactionManager transactionManager,
TransportManager transportManager, Config config)
throws SocketException {
super(datagramSocket.getLocalPort(), transactionManager,
transportManager, config);
this.datagramSocket = datagramSocket;
}
@Override
protected void listen() throws IOException {
byte[] buf = new byte[BUFFER_SIZE];
final DatagramPacket packet = new DatagramPacket(buf, buf.length);
final int noException = 0;
final int socketTimeoutException = 1;
final int ioException = 2;
// AccessController.doPrivileged added for plugin compatibility
int result = AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
try {
datagramSocket.receive(packet);
} catch (SocketTimeoutException e) {
return socketTimeoutException;
} catch (IOException e) {
Logger.error("cannot receive packet", e);
return ioException;
}
return noException;
}
});
switch (result) {
case socketTimeoutException:
return;
case ioException:
throw new IOException();
case noException:
break;
default:
break;
}
byte[] trimmedPacket = new byte[packet.getLength()];
System.arraycopy(packet.getData(), 0,
trimmedPacket, 0, trimmedPacket.length);
processMessage(trimmedPacket, packet.getAddress(),
packet.getPort(), RFC3261.TRANSPORT_UDP);
}
}

View File

@@ -0,0 +1,88 @@
/*
This file is part of Peers, a java SIP softphone.
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 3 of the License, or
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, see <http://www.gnu.org/licenses/>.
Copyright 2007, 2008, 2009, 2010 Yohann Martineau
*/
package peers.sip.transport;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.pmw.tinylog.Logger;
import peers.Config;
import peers.sip.RFC3261;
public class UdpMessageSender extends MessageSender {
private DatagramSocket datagramSocket;
public UdpMessageSender(InetAddress inetAddress, int port,
DatagramSocket datagramSocket, Config config) throws SocketException {
super(datagramSocket.getLocalPort(), inetAddress, port,
config, RFC3261.TRANSPORT_UDP);
this.datagramSocket = datagramSocket;
}
@Override
public synchronized void sendMessage(SipMessage sipMessage) throws IOException {
Logger.debug("UdpMessageSender.sendMessage");
if (sipMessage == null) {
return;
}
byte[] buf = sipMessage.toString().getBytes();
sendBytes(buf);
StringBuffer direction = new StringBuffer();
direction.append("SENT to ").append(inetAddress.getHostAddress());
direction.append("/").append(port);
Logger.info(new String(buf), direction.toString());
}
@Override
public synchronized void sendBytes(byte[] bytes) throws IOException {
Logger.debug("UdpMessageSender.sendBytes");
final DatagramPacket packet = new DatagramPacket(bytes, bytes.length,
inetAddress, port);
Logger.debug("UdpMessageSender.sendBytes " + bytes.length
+ " " + inetAddress + ":" + port);
// AccessController.doPrivileged added for plugin compatibility
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
@Override
public Void run() {
try {
Logger.debug(datagramSocket.getLocalAddress().toString());
datagramSocket.send(packet);
} catch (Throwable t) {
Logger.error("throwable", new Exception(t));
}
return null;
}
}
);
Logger.debug("UdpMessageSender.sendBytes packet sent");
}
}