248 lines
8.3 KiB
Java
248 lines
8.3 KiB
Java
/*
|
|
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.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;
|
|
}
|
|
|
|
}
|