adaptation of old eclipse code to Intellij
This commit is contained in:
43
src/peers/sip/AbstractState.java
Normal file
43
src/peers/sip/AbstractState.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
29
src/peers/sip/JavaUtils.java
Normal file
29
src/peers/sip/JavaUtils.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
47
src/peers/sip/RFC2617.java
Normal file
47
src/peers/sip/RFC2617.java
Normal 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
169
src/peers/sip/RFC3261.java
Normal 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
128
src/peers/sip/Utils.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
300
src/peers/sip/core/useragent/ChallengeManager.java
Normal file
300
src/peers/sip/core/useragent/ChallengeManager.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
312
src/peers/sip/core/useragent/InitialRequestManager.java
Normal file
312
src/peers/sip/core/useragent/InitialRequestManager.java
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
27
src/peers/sip/core/useragent/MessageInterceptor.java
Normal file
27
src/peers/sip/core/useragent/MessageInterceptor.java
Normal 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);
|
||||
}
|
||||
251
src/peers/sip/core/useragent/MidDialogRequestManager.java
Normal file
251
src/peers/sip/core/useragent/MidDialogRequestManager.java
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
136
src/peers/sip/core/useragent/RequestManager.java
Normal file
136
src/peers/sip/core/useragent/RequestManager.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
46
src/peers/sip/core/useragent/SipEvent.java
Normal file
46
src/peers/sip/core/useragent/SipEvent.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
44
src/peers/sip/core/useragent/SipListener.java
Normal file
44
src/peers/sip/core/useragent/SipListener.java
Normal 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);
|
||||
|
||||
}
|
||||
227
src/peers/sip/core/useragent/UAC.java
Normal file
227
src/peers/sip/core/useragent/UAC.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
133
src/peers/sip/core/useragent/UAS.java
Normal file
133
src/peers/sip/core/useragent/UAS.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
382
src/peers/sip/core/useragent/UserAgent.java
Normal file
382
src/peers/sip/core/useragent/UserAgent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
183
src/peers/sip/core/useragent/handlers/ByeHandler.java
Normal file
183
src/peers/sip/core/useragent/handlers/ByeHandler.java
Normal 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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
172
src/peers/sip/core/useragent/handlers/CancelHandler.java
Normal file
172
src/peers/sip/core/useragent/handlers/CancelHandler.java
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
283
src/peers/sip/core/useragent/handlers/DialogMethodHandler.java
Normal file
283
src/peers/sip/core/useragent/handlers/DialogMethodHandler.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
693
src/peers/sip/core/useragent/handlers/InviteHandler.java
Normal file
693
src/peers/sip/core/useragent/handlers/InviteHandler.java
Normal 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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
86
src/peers/sip/core/useragent/handlers/MethodHandler.java
Normal file
86
src/peers/sip/core/useragent/handlers/MethodHandler.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
83
src/peers/sip/core/useragent/handlers/OptionsHandler.java
Normal file
83
src/peers/sip/core/useragent/handlers/OptionsHandler.java
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
293
src/peers/sip/core/useragent/handlers/RegisterHandler.java
Normal file
293
src/peers/sip/core/useragent/handlers/RegisterHandler.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
66
src/peers/sip/syntaxencoding/NameAddress.java
Normal file
66
src/peers/sip/syntaxencoding/NameAddress.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/peers/sip/syntaxencoding/RunnableSipParser.java
Normal file
29
src/peers/sip/syntaxencoding/RunnableSipParser.java
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
54
src/peers/sip/syntaxencoding/SipHeader.java
Normal file
54
src/peers/sip/syntaxencoding/SipHeader.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
49
src/peers/sip/syntaxencoding/SipHeaderFieldMultiValue.java
Normal file
49
src/peers/sip/syntaxencoding/SipHeaderFieldMultiValue.java
Normal 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);
|
||||
}
|
||||
}
|
||||
63
src/peers/sip/syntaxencoding/SipHeaderFieldName.java
Normal file
63
src/peers/sip/syntaxencoding/SipHeaderFieldName.java
Normal 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;
|
||||
}
|
||||
}
|
||||
103
src/peers/sip/syntaxencoding/SipHeaderFieldValue.java
Normal file
103
src/peers/sip/syntaxencoding/SipHeaderFieldValue.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
56
src/peers/sip/syntaxencoding/SipHeaderParamName.java
Normal file
56
src/peers/sip/syntaxencoding/SipHeaderParamName.java
Normal 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;
|
||||
}
|
||||
}
|
||||
102
src/peers/sip/syntaxencoding/SipHeaders.java
Normal file
102
src/peers/sip/syntaxencoding/SipHeaders.java
Normal 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();
|
||||
}
|
||||
}
|
||||
53
src/peers/sip/syntaxencoding/SipHeadersTable.java
Normal file
53
src/peers/sip/syntaxencoding/SipHeadersTable.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
189
src/peers/sip/syntaxencoding/SipParser.java
Normal file
189
src/peers/sip/syntaxencoding/SipParser.java
Normal 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);
|
||||
}
|
||||
}
|
||||
42
src/peers/sip/syntaxencoding/SipParserException.java
Normal file
42
src/peers/sip/syntaxencoding/SipParserException.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
136
src/peers/sip/syntaxencoding/SipURI.java
Normal file
136
src/peers/sip/syntaxencoding/SipURI.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
43
src/peers/sip/syntaxencoding/SipUriSyntaxException.java
Normal file
43
src/peers/sip/syntaxencoding/SipUriSyntaxException.java
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
29
src/peers/sip/transaction/ClientTransaction.java
Normal file
29
src/peers/sip/transaction/ClientTransaction.java
Normal 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();
|
||||
}
|
||||
31
src/peers/sip/transaction/ClientTransactionUser.java
Normal file
31
src/peers/sip/transaction/ClientTransactionUser.java
Normal 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();
|
||||
}
|
||||
247
src/peers/sip/transaction/InviteClientTransaction.java
Normal file
247
src/peers/sip/transaction/InviteClientTransaction.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
45
src/peers/sip/transaction/InviteClientTransactionState.java
Normal file
45
src/peers/sip/transaction/InviteClientTransactionState.java
Normal 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() {}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
179
src/peers/sip/transaction/InviteServerTransaction.java
Normal file
179
src/peers/sip/transaction/InviteServerTransaction.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
46
src/peers/sip/transaction/InviteServerTransactionState.java
Normal file
46
src/peers/sip/transaction/InviteServerTransactionState.java
Normal 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() {}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
37
src/peers/sip/transaction/InviteTransaction.java
Normal file
37
src/peers/sip/transaction/InviteTransaction.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
202
src/peers/sip/transaction/NonInviteClientTransaction.java
Normal file
202
src/peers/sip/transaction/NonInviteClientTransaction.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
126
src/peers/sip/transaction/NonInviteServerTransaction.java
Normal file
126
src/peers/sip/transaction/NonInviteServerTransaction.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
35
src/peers/sip/transaction/NonInviteTransaction.java
Normal file
35
src/peers/sip/transaction/NonInviteTransaction.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
32
src/peers/sip/transaction/ServerTransaction.java
Normal file
32
src/peers/sip/transaction/ServerTransaction.java
Normal 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);
|
||||
}
|
||||
25
src/peers/sip/transaction/ServerTransactionUser.java
Normal file
25
src/peers/sip/transaction/ServerTransactionUser.java
Normal 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();
|
||||
}
|
||||
64
src/peers/sip/transaction/SipListeningPoint.java
Normal file
64
src/peers/sip/transaction/SipListeningPoint.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
77
src/peers/sip/transaction/Transaction.java
Normal file
77
src/peers/sip/transaction/Transaction.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
209
src/peers/sip/transaction/TransactionManager.java
Normal file
209
src/peers/sip/transaction/TransactionManager.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
259
src/peers/sip/transactionuser/Dialog.java
Normal file
259
src/peers/sip/transactionuser/Dialog.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
115
src/peers/sip/transactionuser/DialogManager.java
Normal file
115
src/peers/sip/transactionuser/DialogManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
40
src/peers/sip/transactionuser/DialogState.java
Normal file
40
src/peers/sip/transactionuser/DialogState.java
Normal 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() {}
|
||||
|
||||
}
|
||||
55
src/peers/sip/transactionuser/DialogStateConfirmed.java
Normal file
55
src/peers/sip/transactionuser/DialogStateConfirmed.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
54
src/peers/sip/transactionuser/DialogStateEarly.java
Normal file
54
src/peers/sip/transactionuser/DialogStateEarly.java
Normal 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();
|
||||
}
|
||||
}
|
||||
54
src/peers/sip/transactionuser/DialogStateInit.java
Normal file
54
src/peers/sip/transactionuser/DialogStateInit.java
Normal 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();
|
||||
}
|
||||
}
|
||||
55
src/peers/sip/transactionuser/DialogStateTerminated.java
Normal file
55
src/peers/sip/transactionuser/DialogStateTerminated.java
Normal 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();
|
||||
}
|
||||
}
|
||||
205
src/peers/sip/transport/MessageReceiver.java
Normal file
205
src/peers/sip/transport/MessageReceiver.java
Normal 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;
|
||||
// }
|
||||
|
||||
}
|
||||
102
src/peers/sip/transport/MessageSender.java
Normal file
102
src/peers/sip/transport/MessageSender.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
27
src/peers/sip/transport/SipClientTransportUser.java
Normal file
27
src/peers/sip/transport/SipClientTransportUser.java
Normal 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);
|
||||
}
|
||||
80
src/peers/sip/transport/SipMessage.java
Normal file
80
src/peers/sip/transport/SipMessage.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
53
src/peers/sip/transport/SipRequest.java
Normal file
53
src/peers/sip/transport/SipRequest.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
50
src/peers/sip/transport/SipResponse.java
Normal file
50
src/peers/sip/transport/SipResponse.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/peers/sip/transport/SipServerTransportUser.java
Normal file
25
src/peers/sip/transport/SipServerTransportUser.java
Normal 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);
|
||||
}
|
||||
123
src/peers/sip/transport/SipTransportConnection.java
Normal file
123
src/peers/sip/transport/SipTransportConnection.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
479
src/peers/sip/transport/TransportManager.java
Normal file
479
src/peers/sip/transport/TransportManager.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
90
src/peers/sip/transport/UdpMessageReceiver.java
Normal file
90
src/peers/sip/transport/UdpMessageReceiver.java
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
88
src/peers/sip/transport/UdpMessageSender.java
Normal file
88
src/peers/sip/transport/UdpMessageSender.java
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user