package org.mobicents.slee.resource.sip11.wrappers; import gov.nist.javax.sip.address.GenericURI; import gov.nist.javax.sip.header.CSeq; import gov.nist.javax.sip.message.SIPRequest; import java.rmi.server.UID; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import javax.sip.ClientTransaction; import javax.sip.Dialog; import javax.sip.DialogDoesNotExistException; import javax.sip.DialogState; import javax.sip.DialogTerminatedEvent; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ResponseEvent; import javax.sip.ServerTransaction; import javax.sip.SipException; import javax.sip.Transaction; import javax.sip.TransactionDoesNotExistException; import javax.sip.TransactionUnavailableException; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; import javax.sip.header.CallIdHeader; import javax.sip.header.ContactHeader; import javax.sip.header.ContentTypeHeader; import javax.sip.header.FromHeader; import javax.sip.header.Header; import javax.sip.header.HeaderFactory; import javax.sip.header.MaxForwardsHeader; import javax.sip.header.Parameters; import javax.sip.header.RecordRouteHeader; import javax.sip.header.RouteHeader; import javax.sip.header.ToHeader; import javax.sip.header.ViaHeader; import javax.sip.message.Message; import javax.sip.message.Request; import javax.sip.message.Response; import javax.slee.AddressPlan; import javax.slee.facilities.Tracer; import javax.slee.resource.FireableEventType; import net.java.slee.resource.sip.DialogActivity; import net.java.slee.resource.sip.DialogForkedEvent; import org.mobicents.slee.resource.sip11.SipActivityHandle; import org.mobicents.slee.resource.sip11.SipResourceAdaptor; import org.mobicents.slee.resource.sip11.SleeSipProviderImpl; public class DialogWrapper implements DialogActivity, WrapperSuperInterface { /** * */ private static final long serialVersionUID = 1L; public final static Set<String> dialogCreatingMethods; static { Set<String> set = new HashSet<String>(); set.add(Request.INVITE); set.add(Request.REFER); set.add(Request.SUBSCRIBE); dialogCreatingMethods = Collections.unmodifiableSet(set); } protected javax.sip.Dialog wrappedDialog = null; protected SipActivityHandle sipActivityHandle; protected String initiatingTransctionId = null; protected SipResourceAdaptor ra; protected ConcurrentHashMap<String, ClientTransaction> ongoingClientTransactions = new ConcurrentHashMap<String, ClientTransaction>(); protected ConcurrentHashMap<String, ServerTransaction> ongoingServerTransactions = new ConcurrentHashMap<String, ServerTransaction>(); protected String lastCancelableTransactionId = null; // Forging kit? protected SleeSipProviderImpl provider = null; private static Tracer tracer; // ###################### // # STRICTLY FORK AREA # // ###################### private enum DialogForkState { AWAIT_FIRST_TAG, AWAIT_FINAL, END } // Comment: JSIP set ToTag and route list on first provisional response // with ToTag and 2xx, so we have to keep track of those for each activity, // in order to allow it to send responses to proper peer. // This is due to having one SIPDialog for all forks - this is standard for // no auto dialog support. /** * Indicates sip activity handle for initial dialog activity created for * this dialog - created as first. Child dialogs are represented by dialog * activities with the same wrappedDialog object. * wrappedDialog.getApplicationData() returns always initial dialog activity * or one that won race to get 2xx response */ SipActivityHandle forkInitialActivityHandle = null; protected DialogForkState forkState = DialogForkState.AWAIT_FIRST_TAG; /** * Used when: {@link #forkInitialActivityHandle} !=null or Dialog is null. */ protected ArrayList<RouteHeader> localRouteSet = new ArrayList<RouteHeader>(); /** * Used to detect how route set has been created - in case of responses we want to override * local route set, since in case of forks, route set on request will be propably bad :) */ protected boolean routeSetOnRequest = false; /** * Represents local ToTag - see comment above */ protected String localToTag = null; protected SipURI reqeustURI = null; protected AtomicLong localSequenceNumber= new AtomicLong(0); // private Address localRemoteParty; /** * Cotnains activity handles of fork children. */ protected ConcurrentHashMap<String, SipActivityHandle> toTag2DialogHandle = new ConcurrentHashMap<String, SipActivityHandle>(); public DialogWrapper(SleeSipProviderImpl provider, SipResourceAdaptor ra) { this.provider = provider; this.ra = ra; if (tracer == null) { tracer = ra.getRaContext().getTracer(DialogWrapper.class.getSimpleName()); } // TODO: Come up with something way better, we cant hash on // dialogId, since it changes // We cant make ID inside change, since this would make Container to // leak attached SBBE - we would loose them, as in container view, // hash contract on handle would be broken // and on each change dialog would be considered as different activity sipActivityHandle = new SipActivityHandle(new UID().toString()); //long id = (long) (System.currentTimeMillis() * ((double) Math.random()) * 100000); //long high32 = (id & 0xffffffff00000000L) >> 32; //long low32 = (id & 0xffffffffL); //autoGeneratedFromTag = high32 + "_" + low32; autoGeneratedFromTag = gov.nist.javax.sip.Utils.getInstance().generateTag(); } public DialogWrapper(Dialog wrappedDialog, SipActivityHandle forkInitialActivityHandle, SleeSipProviderImpl provider, SipResourceAdaptor ra) { this(provider, ra); this.wrappedDialog = wrappedDialog; this.forkInitialActivityHandle = forkInitialActivityHandle; if (forkInitialActivityHandle == null) { if (wrappedDialog.getApplicationData() != null) { if (wrappedDialog.getApplicationData() instanceof DialogWrapper) { throw new IllegalArgumentException("Dialog to wrap has alredy a wrapper!!!"); } else { if (tracer.isFineEnabled()) { tracer.fine("Overwriting application data present - " + wrappedDialog.getApplicationData()); } } } this.wrappedDialog.setApplicationData(this); } else { // do nothing } } public void cleanup() { if (wrappedDialog != null && this.forkInitialActivityHandle == null) { this.wrappedDialog.setApplicationData(null); if (!wrappedDialog.isServer()) { String key = this.wrappedDialog.getLocalTag() + "_" + this.wrappedDialog.getCallId().getCallId(); this.ra.removeClientDialogMapping(key); } } this.wrappedDialog = null; this.sipActivityHandle = null; this.ra = null; // FIXE: Clean this ? this.ongoingClientTransactions = null; this.ongoingServerTransactions = null; } private void setLocalSequenceNumber(long lCseq) { if (lCseq <= this.localSequenceNumber.get()) throw new RuntimeException("Sequence number should not decrease !"); this.localSequenceNumber.set(lCseq); } public ClientTransaction getClientTransaction(String id) { return this.ongoingClientTransactions.get(id); } public String getInitiatingTransactionId() { return initiatingTransctionId; } public ServerTransaction getServerTransaction(String id) { return this.ongoingServerTransactions.get(id); } public Request createAck(long arg0) throws InvalidArgumentException, SipException { if(wrappedDialog!=null) { return wrappedDialog.createAck(arg0); }else { throw new SipException("Cant create ACK before initial request has not been sent!!!"); } } public Request createPrack(Response arg0) throws DialogDoesNotExistException, SipException { if(wrappedDialog!=null) { return this.wrappedDialog.createPrack(arg0); }else { throw new SipException("Cant create PRACK before initial request has not been sent!!!"); } } public Response createReliableProvisionalResponse(int arg0) throws InvalidArgumentException, SipException { if(wrappedDialog!=null) { return this.wrappedDialog.createReliableProvisionalResponse(arg0); }else { throw new SipException("Cant create response before initial request has not been sent!!!"); } } public void delete() { // This ensures that dialog will be removed if (wrappedDialog == null) { ra.processDialogTerminated(new DialogTerminatedEvent(this.provider, this)); } else if (this.forkInitialActivityHandle == null) { // We are master, if we die, everything else does // FIXME: is above statement correct? try { this.terminateFork(this.getActivityHandle()); } finally { if (wrappedDialog.getState() == null) { ra.processDialogTerminated(new DialogTerminatedEvent(this.provider, wrappedDialog)); } } wrappedDialog.delete(); } else { ra.processDialogTerminated(new DialogTerminatedEvent(this.provider, this)); } } public Object getApplicationData() { throw new SecurityException(); } public CallIdHeader getCallId() { if(this.wrappedDialog==null) { return callIdToReUse; } return this.wrappedDialog.getCallId(); } public String getDialogId() { if(this.wrappedDialog==null) { return null; } return this.wrappedDialog.getDialogId(); } public Transaction getFirstTransaction() { if(wrappedDialog!=null) { return this.wrappedDialog.getFirstTransaction(); }else { return null; } } // FIXME: for now its ok, only in server mode it would fial, but we dont have fork behaviour for server, nor we can have null wrappedDialog in server dialog :} public Address getLocalParty() { if(wrappedDialog!=null && !isInForkedActions()) { return this.wrappedDialog.getLocalParty(); }else if(wrappedDialog!=null && isInForkedActions()) { return this.fromAddress; }else { return null; } } public Address getRemoteParty() { if(wrappedDialog!=null && !isInForkedActions()) { return this.wrappedDialog.getRemoteParty(); }else if(wrappedDialog!=null && isInForkedActions()) { return this.toAddress; }else { return null; } } public Address getRemoteTarget() { if(wrappedDialog!=null && !isInForkedActions()) { return this.wrappedDialog.getRemoteTarget(); }else if(wrappedDialog!=null && isInForkedActions()) { return this.toAddress!=null?this.toAddress:null; }else { return null; } } public long getLocalSeqNumber() { if(isInForkedActions() || wrappedDialog==null) { return localSequenceNumber.get(); }else { return this.wrappedDialog.getLocalSeqNumber(); } } public int getLocalSequenceNumber() { if(isInForkedActions() || wrappedDialog==null) { return (int) localSequenceNumber.get(); }else { return this.wrappedDialog.getLocalSequenceNumber(); } } public String getLocalTag() { if(wrappedDialog!=null ) { return this.wrappedDialog.getLocalTag(); }else { return autoGeneratedFromTag; } } public long getRemoteSeqNumber() { if(wrappedDialog!=null) return this.wrappedDialog.getRemoteSeqNumber(); else return -1; } public int getRemoteSequenceNumber() { if(wrappedDialog!=null) return this.wrappedDialog.getRemoteSequenceNumber(); else return -1; } public String getRemoteTag() { if(wrappedDialog!=null && !isInForkedActions()) { return this.wrappedDialog.getRemoteTag(); }else if(wrappedDialog!=null && isInForkedActions()) { return this.localToTag; }else { return null; } } public Iterator getRouteSet() { if(wrappedDialog!=null) { return this.wrappedDialog.getRouteSet(); }else { return new ArrayList(this.localRouteSet).iterator(); } } public DialogState getState() { if(this.wrappedDialog==null) { return null; } return this.wrappedDialog.getState(); } public void incrementLocalSequenceNumber() { if(isInForkedActions() || wrappedDialog==null) { localSequenceNumber.incrementAndGet(); }else { this.wrappedDialog.incrementLocalSequenceNumber(); } } public boolean isSecure() { if(wrappedDialog==null) return false; return wrappedDialog.isSecure(); } public boolean isServer() { if(wrappedDialog==null) return false; return wrappedDialog.isServer(); } public void sendAck(Request arg0) throws SipException { if(wrappedDialog==null) { throw new SipException("Wrong state, cant send ack before dialog createing request!!!"); } wrappedDialog.sendAck(arg0); } public void sendReliableProvisionalResponse(Response arg0) throws SipException { if(wrappedDialog==null) { throw new SipException("Wrong state, cant send provisional response before dialog createing request!!!"); } wrappedDialog.sendReliableProvisionalResponse(arg0); } public void setApplicationData(Object arg0) { throw new SecurityException(); } public void terminateOnBye(boolean arg0) throws SipException { wrappedDialog.terminateOnBye(arg0); } public SipActivityHandle getActivityHandle() { return this.sipActivityHandle; } public Object getWrappedObject() { return this.wrappedDialog; } public boolean hasOngoingServerTransaction(String branchID) { return this.ongoingServerTransactions.containsKey(branchID); } public boolean hasOngoingClientTransaction(String branchID) { return this.ongoingClientTransactions.containsKey(branchID); } public void addOngoingTransaction(ServerTransactionWrapper stw) { this.ongoingServerTransactions.putIfAbsent(stw.getBranchId(), stw); if(isInForkedActions()) { //Yup, could be reinveite? we have to update local DW cseq :) setLocalSequenceNumber( ((CSeq)stw.getRequest().getHeader(CSeqHeader.NAME)).getSeqNumber()); } } public void addOngoingTransaction(ClientTransactionWrapper ctw) { this.ongoingClientTransactions.putIfAbsent(ctw.getBranchId(), ctw); } public void removeOngoingTransaction(ClientTransactionWrapper ctw) { // synchronized (this.wrappedDialog) { // if (this.getState() != DialogState.TERMINATED) this.ongoingClientTransactions.remove(ctw.getBranchId()); // } } public void removeOngoingTransaction(ServerTransactionWrapper stw) { // synchronized (this.ongoingServerTransactions) { // if (this.getState() != DialogState.TERMINATED) this.ongoingServerTransactions.remove(stw.getBranchId()); // } } public void removeOngoingTransaction(SuperTransactionWrapper stw) { } public void clearOngoingTransaction() { this.ongoingClientTransactions.clear(); this.ongoingServerTransactions.clear(); } public void clearAssociations() { } public String getLastCancelableTransactionId() { return lastCancelableTransactionId; } // =========================== XXX: Helper methods ===================== private void forgeMessage(Message originalMessage, Message forgedMessage, Set<String> headerstoOmmit) throws SipException { // We leave to and from with tags from this dialog, but copy all other // parameters // Route headers are from this dialog ListIterator lit = originalMessage.getHeaderNames(); while (lit.hasNext()) { String headerName = (String) lit.next(); // FIXME: Could be wrong - but, if there is value in forgedRequest, // we leave original value // FIXME: What about address URI parameters? // FIXME: What about MaxForwards? if (headerName.equals(ToHeader.NAME) || headerName.equals(FromHeader.NAME)) { Parameters origHeader, forgedHeader; origHeader = (Parameters) originalMessage.getHeader(headerName); forgedHeader = (Parameters) forgedMessage.getHeader(headerName); Iterator it = origHeader.getParameterNames(); Set<String> toOmmit = new HashSet<String>(); toOmmit.add("tag"); copyParameters(headerName, origHeader, forgedHeader, toOmmit); } else if (headerstoOmmit.contains(headerName)) { continue; } else { // FIXME: For now we simply copy everything, overwrte existing // headers if (forgedMessage.getHeaders(headerName).hasNext()) forgedMessage.removeHeader(headerName); ListIterator headersIterator = originalMessage.getHeaders(headerName); while (headersIterator.hasNext()) { Header origHeader, forgedHeader; // origHeader = (Header) // originalMessage.getHeader(headerName); origHeader = (Header) headersIterator.next(); forgedHeader = null; try { forgedHeader = (Header) this.provider.getHeaderFactory().createHeader(headerName, origHeader.toString().substring(origHeader.toString().indexOf(":") + 1)); forgedMessage.addLast((javax.sip.header.Header) forgedHeader); } catch (ParseException e) { tracer.severe("Failed to generate header on [" + headerName + "]. To copy value [" + origHeader + "]\n", e); throw new SipException("Major failure", e); } } } } // Copy content byte[] rawOriginal = originalMessage.getRawContent(); if (rawOriginal != null && rawOriginal.length != 0) { byte[] copy = new byte[rawOriginal.length]; System.arraycopy(rawOriginal, 0, copy, 0, copy.length); try { forgedMessage.setContent(new String(copy), (ContentTypeHeader) forgedMessage .getHeader(ContentTypeHeader.NAME)); } catch (ParseException e) { tracer.severe("Failed to set content on forged message. To copy value [" + new String(copy) + "] Type [" + forgedMessage.getHeader(ContentTypeHeader.NAME) + "]\n", e); } } } private void copyParameters(String name, Parameters origHeader, Parameters forgedHeader, Set<String> toOmmit) { // FIXME: This will fail for parameters such as lr ?? Iterator it = origHeader.getParameterNames(); while (it.hasNext()) { String p_name = (String) it.next(); if (toOmmit.contains(p_name) || forgedHeader.getParameter(p_name) != null) { if (tracer.isFineEnabled()) { tracer.fine("Ommiting parameter on [" + name + "]. To copy value [" + origHeader.getParameter(p_name) + "]\nValue in forged [" + forgedHeader.getParameter(p_name) + "]"); } } else { try { forgedHeader.setParameter(p_name, origHeader.getParameter(p_name)); } catch (ParseException e) { tracer.severe("Failed to pass parameter on [" + name + "]. To copy value [" + origHeader.getParameter(p_name) + "]\nValue in forged [" + forgedHeader.getParameter(p_name) + "]", e); } } } } public String toString() { return "Dialog Id[" + this.getDialogId() + "] State[" + this.getState() + "] OngoingCTX[" + this.ongoingClientTransactions.size() + "] OngoingSTX[" + this.ongoingServerTransactions.size() + "]"; } // ########################################### // # Strictly DialogActivity defined methods # // ########################################### public ClientTransaction sendCancel() throws SipException { verifyDialogExistency(); try { ClientTransaction inviteCTX = this.getClientTransaction(lastCancelableTransactionId); Request cancelRequest = inviteCTX.createCancel(); ClientTransaction cancelTransaction = this.provider.getNewClientTransaction(cancelRequest, false); cancelTransaction.sendRequest(); return cancelTransaction; } catch (NullPointerException npe) { if (tracer.isFineEnabled()) { tracer.fine(npe.getMessage(),npe); } throw new SipException("Possibly fialed to obtain client transaction or no INVITE transaction present"); } catch (Exception e) { throw new SipException("Failed to send CANCEL due to:", e); } } public void associateServerTransaction(ClientTransaction ct, ServerTransaction st) { // ct MUST be in ongoing transaction, its local, st - comes from another // dialog verifyDialogExistency(); if (!this.hasOngoingClientTransaction(ct.getBranchId())) { throw new IllegalArgumentException("Client transaction is not in ongoing transaction list!!!"); } if (st != null) { if (!((DialogWrapper) st.getDialog()).hasOngoingServerTransaction(st.getBranchId())) { throw new IllegalArgumentException("Server transaction is not in ongoing transaction list!!!"); } // XXX: ctx can be associated to only one stx, however stx can have // multiple ctx ((ClientTransactionWrapper) ct).associateServerTransaction(st.getBranchId(), ((DialogWrapper) st.getDialog()).getActivityHandle()); } else { ((ClientTransactionWrapper) ct).associateServerTransaction(null, null); } } public Request createRequest(String arg0) throws SipException { final HeaderFactory headerFactory = provider.getHeaderFactory(); if (this.wrappedDialog == null) { // the real dialog doesn't exist yet so we act like we will build such a dialog when sending this request try { // From Header: FromHeader fromHeader = headerFactory.createFromHeader( fromAddress, autoGeneratedFromTag); // To header and request URI: ToHeader toHeader = headerFactory.createToHeader(toAddress, null); javax.sip.address.URI requestURI = toAddress.getURI(); // Create the Via header and add to an array list ListeningPoint listeningPoint = provider.getListeningPoints()[0]; ViaHeader viaHeader = headerFactory.createViaHeader( listeningPoint.getIPAddress(), listeningPoint.getPort(), listeningPoint.getTransport(), null); ArrayList<ViaHeader> viaHeadersList = new ArrayList<ViaHeader>(); viaHeadersList.add(viaHeader); MaxForwardsHeader maxForwardsHeader = headerFactory.createMaxForwardsHeader(70); // Cseq header: CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(localSequenceNumber.get()+1, arg0); //cSeqHeader.setMethod(arg0); // create the request Request request= provider.getMessageFactory().createRequest((javax.sip.address.URI) requestURI.clone(),arg0, callIdToReUse, cSeqHeader, fromHeader, toHeader, viaHeadersList, maxForwardsHeader); //baranowb: this propably wont be triggered ever. fillRequestHeaders(request); return request; }catch (Exception e) { throw new SipException(e.getMessage(),e); } } else if(isInForkedActions() && !wrappedDialog.isServer()) { try{ Request req=this.wrappedDialog.createRequest(arg0); req.removeHeader(RouteHeader.NAME); ListeningPoint listeningPoint = provider.getListeningPoints()[0]; ViaHeader viaHeader = headerFactory.createViaHeader( listeningPoint.getIPAddress(), listeningPoint.getPort(), listeningPoint.getTransport(), null); req.addFirst(viaHeader); CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(localSequenceNumber.get()+1, null); cSeqHeader.setMethod(arg0); fillRequestHeaders(req); //this.localSequenceNumber++; //CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(localSequenceNumber, arg0); //req.addHeader(cSeqHeader); return req; }catch (Exception e) { throw new SipException(e.getMessage(),e); } }else { try{ //XXX: here we dont have to set anything, we are not forking so its ok. Request req=this.wrappedDialog.createRequest(arg0); ListeningPoint listeningPoint = provider.getListeningPoints()[0]; ViaHeader viaHeader = headerFactory.createViaHeader( listeningPoint.getIPAddress(), listeningPoint.getPort(), listeningPoint.getTransport(), null); req.addFirst(viaHeader); return req; }catch (Exception e) { throw new SipException(e.getMessage(),e); } } } public Request createRequest(Request origRequest) throws SipException { generateRouteList(origRequest); Set<String> headersToOmmit = new HashSet<String>(); Request forgedRequest = null; if (this.wrappedDialog != null) { if(!origRequest.getMethod().equals(Request.ACK)) { forgedRequest = this.wrappedDialog.createRequest(origRequest.getMethod()); }else { try { forgedRequest = this.wrappedDialog.createAck(this.wrappedDialog.getLocalSeqNumber()); } catch (InvalidArgumentException e) { throw new SipException("",e); } } //Here we have to check if this is in foking condition, in this case, we have to change: // - route set // - callid // - from,to } else { try { MaxForwardsHeader mf = this.provider.getHeaderFactory().createMaxForwardsHeader(70); forgedRequest = this.provider.getMessageFactory().createRequest(null); forgedRequest.setRequestURI(origRequest.getRequestURI()); forgedRequest.addHeader(mf); forgedRequest.addHeader(callIdToReUse); CSeqHeader cseqHeader=(CSeqHeader) origRequest.getHeader(CSeqHeader.NAME); cseqHeader=(CSeqHeader) cseqHeader.clone(); forgedRequest.addHeader(cseqHeader); if(forgedRequest.getMethod()==null) ((SIPRequest) forgedRequest).setMethod(cseqHeader.getMethod()); } catch (Exception e) { throw new SipException("", e); } } fillRequestHeaders(forgedRequest); headersToOmmit.add(RouteHeader.NAME); headersToOmmit.add(RecordRouteHeader.NAME); // headersToOmmit.add(ViaHeader.NAME); headersToOmmit.add(CallIdHeader.NAME); headersToOmmit.add(CSeqHeader.NAME); forgeMessage(origRequest, forgedRequest, headersToOmmit); //FIXME: ??? forgedRequest.addFirst(provider.getLocalVia(this.provider.getListeningPoints()[0].getTransport(), null)); return forgedRequest; } public ClientTransaction sendRequest(Request request) throws SipException, TransactionUnavailableException { if (wrappedDialog == null && !dialogCreatingMethods.contains(request.getMethod())) { throw new IllegalStateException("Dialog activity present, but no dialog creating reqeust has been sent yet! This method: " + request.getMethod() + " is not dialog creating one"); } ClientTransactionWrapper CTW = (ClientTransactionWrapper) this.provider.getNewClientTransaction(request, false); boolean created=false; if (request.getMethod().compareTo(Request.INVITE)==0) lastCancelableTransactionId = CTW.getBranchId(); if (wrappedDialog == null) { this.wrappedDialog = this.provider.getNewDialog(CTW,null, false); this.wrappedDialog.setApplicationData(this); created=true; } this.addOngoingTransaction(CTW); String method=request.getMethod(); if(isInForkedActions() ) { //cause dialog spoils - changes cseq, we dont want that CTW.sendRequest(); if( method.compareTo(Request.ACK)!=0 && method.compareTo(Request.CANCEL)!=0) localSequenceNumber.incrementAndGet(); }else { //JSIP does not allow to send via dialog on null state, yet :) if(!created) { this.wrappedDialog.sendRequest((ClientTransaction) CTW.getWrappedTransaction()); }else { CTW.sendRequest(); } } return CTW; } public void sendRequest(ClientTransaction ctw) throws TransactionDoesNotExistException, SipException { if(wrappedDialog==null ) { Request request=ctw.getRequest(); if (wrappedDialog == null && !dialogCreatingMethods.contains(request.getMethod())) { throw new IllegalStateException("Dialog activity present, but no dialog creating reqeust has been sent yet! This method: " + request.getMethod() + " is not dialog creating one"); } ClientTransactionWrapper CTW = (ClientTransactionWrapper) ctw; boolean created=false; if (request.getMethod().equals(Request.INVITE)) lastCancelableTransactionId = CTW.getBranchId(); if (wrappedDialog == null) { this.wrappedDialog = this.provider.getNewDialog(CTW,null, false); this.wrappedDialog.setApplicationData(this); } this.addOngoingTransaction(CTW); //JSIP does not allow to send via dialog on null state, yet :) if(!created) { this.wrappedDialog.sendRequest((ClientTransaction) CTW.getWrappedTransaction()); }else { CTW.sendRequest(); } }else if(isInForkedActions()) { Request r=ctw.getRequest(); //CSeqHeader cseq=(CSeqHeader) ctw.getRequest().getHeader(CSeqHeader.NAME); //if(cseq==null) // throw new SipException("Cseq cant be null at this point!"); if(isInForkedActions() && r.getMethod().compareTo(Request.ACK)!=0 && r.getMethod().compareTo(Request.CANCEL)!=0 ) localSequenceNumber.incrementAndGet(); //try { // cseq.setSeqNumber(++localSequenceNumber); //} catch (InvalidArgumentException e) { // TODO Auto-generated catch block // e.printStackTrace(); //} ctw.sendRequest(); }else { wrappedDialog.sendRequest((ClientTransaction) ((ClientTransactionWrapper) ctw).getWrappedTransaction()); } } public Response createResponse(ServerTransaction origServerTransaction, Response receivedResponse) throws SipException { if (!this.hasOngoingServerTransaction(origServerTransaction.getBranchId())) throw new IllegalArgumentException("Passed server transaction is not in ongoing STX list for this dialog!!!!"); Response forgedResponse; try { // FIXME check this works as expected forgedResponse = this.provider.getMessageFactory().createResponse(receivedResponse.getStatusCode(), origServerTransaction.getRequest()); } catch (ParseException e) { e.printStackTrace(); throw new SipException("Failed to forge message", e); } Set<String> headersToOmmit = new HashSet<String>(); headersToOmmit.add(RouteHeader.NAME); headersToOmmit.add(RecordRouteHeader.NAME); headersToOmmit.add(ViaHeader.NAME); headersToOmmit.add(CallIdHeader.NAME); headersToOmmit.add(CSeqHeader.NAME); headersToOmmit.add(ContactHeader.NAME); forgeMessage(receivedResponse, forgedResponse, headersToOmmit); //if(this.wrappedDialog==null) //{ // forgedResponse.addHeader(this.provider.getHeaderFactory().createContactHeader(this.getLocalParty())); //}else { //This is ok, we receveid this dialog on one LP, we need to put Contact there. Request origRequest = origServerTransaction.getRequest(); ViaHeader topVia = (ViaHeader) origRequest.getHeader(ViaHeader.NAME); if(topVia!=null) { Address address = getLocalAddressForTransport(topVia.getTransport()); if(address !=null) { ContactHeader contactHeader = this.provider.getHeaderFactory().createContactHeader(address); forgedResponse.addLast(contactHeader); }else { if(tracer.isFineEnabled()) { tracer.fine("Failed to obtain contact address for AS, can not compute AS contact."); } } }else { if(tracer.isFineEnabled()) { tracer.fine("There is no via header, can not compute AS contact."); } } } //If we are client Dialog, we can receive response with ToTag, when local is not present, we shoudl copy ToHeader localToHeader=(ToHeader) forgedResponse.getHeader(ToHeader.NAME); //System.out.println("{"+(localToHeader.getTag()==null)+"}{"+(!this.isServer())+"}"); if(localToHeader.getTag()==null && this.isServer()) { ToHeader otherToHeader=(ToHeader) receivedResponse.getHeader(ToHeader.NAME); try { localToHeader.setTag(otherToHeader.getTag()); } catch (ParseException e) { e.printStackTrace(); throw new SipException("Failed to set ToTag", e); } } return forgedResponse; } public ServerTransaction getAssociatedServerTransaction(ClientTransaction ct) { if (!this.hasOngoingClientTransaction(ct.getBranchId())) { throw new IllegalArgumentException("Passed client transaction is not running for this dialog"); } ClientTransactionWrapper ctw = (ClientTransactionWrapper) ct; if (ctw.getAssociatedTransactionBranchId() == null || ra.getActivity(ctw.getAssociationHandle()) == null) { return null; } else { DialogWrapper da = (DialogWrapper) ra.getActivity(ctw.getAssociationHandle()); return da.getServerTransaction(ctw.getAssociatedTransactionBranchId()); } } // ############################################################## // # Strictly dialog forge - used when no dialog is present yet # // ############################################################## protected Address fromAddress, toAddress; protected CallIdHeader callIdToReUse = null; protected String autoGeneratedFromTag = null; public void setFromAddress(Address fromAddress) { this.fromAddress = fromAddress; } public void setToAddress(Address toAddress) { this.toAddress = toAddress; } public void setCallIdToReUse(CallIdHeader callIdToReUse) { this.callIdToReUse = callIdToReUse; } private void verifyDialogExistency() { if (wrappedDialog == null) { throw new IllegalStateException("Dialog activity present, but no dialog creating reqeust has been sent yet!"); } } /** * This should be used on reqeusts when wrapped dialog is null - this DW was * created by getNewDialog(from,to) or * getNewDialog(incomingDialog,reuseCallId). * * @param request * @throws SipException */ private void fillRequestHeaders(Request request) throws SipException { String method = request.getMethod(); try { if ((isInForkedActions()) && wrappedDialog!=null && !wrappedDialog.isServer()) { // We have wrappedDialog, but we cant relly on its info, // only CallId, from address, to addres, fromheader are // static request.removeHeader(CallIdHeader.NAME); request.addLast(wrappedDialog.getCallId()); ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME); FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); if (toHeader == null || !toHeader.getAddress().equals(wrappedDialog.getRemoteParty())) toHeader = this.provider.getHeaderFactory().createToHeader(toAddress, null); if (localToTag != null && ! (localToTag.compareTo(toHeader.getTag())==0)) { toHeader.setTag(localToTag); } request.removeHeader(toHeader.NAME); request.addHeader(toHeader); if (fromHeader == null || !fromHeader.getAddress().equals(wrappedDialog.getLocalParty())) fromHeader = this.provider.getHeaderFactory().createFromHeader(wrappedDialog.getLocalParty(), wrappedDialog.getLocalTag()); request.removeHeader(fromHeader.NAME); request.addHeader(fromHeader); if (localRouteSet.size() > 0 ) { if((request.getHeaders(RouteHeader.NAME)!=null && !request.getHeaders(RouteHeader.NAME).hasNext())) request.removeHeader(RouteHeader.NAME); for (int i = 0; i < localRouteSet.size(); i++) { request.addLast(localRouteSet.get(i)); //request.addHeader(forkRouteSet.get(i)); } } SipURI cloned=(SipURI) this.reqeustURI.clone(); request.setRequestURI(cloned); CSeqHeader cseq=(CSeqHeader) request.getHeader(CSeq.NAME); if(cseq==null) { throw new SipException("CSeq cant be null at this point!!"); } //we leave this as it was if (!method.equals(Request.CANCEL) && !method.equals(Request.ACK)) cseq.setSeqNumber(this.localSequenceNumber.get()+1); cloned.setMethodParam(cseq.getMethod()); } else if (!isInForkedActions() && wrappedDialog!=null && wrappedDialog.isServer()) { } else if(wrappedDialog==null){ //FIXME: this can be wrong request.removeHeader(CallIdHeader.NAME); request.addLast(callIdToReUse); ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME); FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); if (toHeader == null) { toHeader = this.provider.getHeaderFactory().createToHeader(toAddress, null); } else { toHeader.setAddress(toAddress); if (wrappedDialog != null) { toHeader.setTag(wrappedDialog.getRemoteTag()); } } request.removeHeader(toHeader.NAME); request.addLast(toHeader); if (fromHeader == null || !fromHeader.getAddress().equals(fromAddress)) { fromHeader = this.provider.getHeaderFactory().createFromHeader(fromAddress, autoGeneratedFromTag); } else { fromHeader.setAddress(fromAddress); fromHeader.setTag(autoGeneratedFromTag); } request.removeHeader(fromHeader.NAME); request.addLast(fromHeader); // route header set process if (localRouteSet.size() > 0 ) { if((request.getHeaders(RouteHeader.NAME)!=null && !request.getHeaders(RouteHeader.NAME).hasNext())) request.removeHeader(RouteHeader.NAME); for (int i = 0; i < localRouteSet.size(); i++) { request.addLast(localRouteSet.get(i)); //request.addHeader(forkRouteSet.get(i)); } } }else { if(tracer.isFineEnabled()) { //THis is triggered anyway.... tracer.fine("No need to fill request headers, route set and other state should be maintaned by stack: "+this); } } } catch (Exception e) { throw new SipException("Couldnt add specs headers due to:", e); } } /** * Does response processing in dialog * * @param respEvent * @return <ul> * <li><b>true</b> - if DialogWrapper finished processing and RA * doesnt have to take any action</li> * <li><b>false</b> - DialogWrapper did nothing, ra should triger * regular logic</li> * </ul> */ public boolean processIncomingResponse(ResponseEvent respEvent) { // public synchronized boolean processIncomingResponse(ResponseEvent // respEvent) { // FIXME: not very good idea to cross message sending logic between RA // and DW // possibly it would be better to move indialog sending logic entirely // to DW ? // But we need it only in cases DW ends,has to send message to child or // indicate forking Response response = respEvent.getResponse(); boolean firedByDialogWrapper = false; int statusCode = response.getStatusCode(); String toTag = ((ToHeader) response.getHeader(ToHeader.NAME)).getTag(); DialogForkState oldForkState = this.forkState; SipActivityHandle masterActivityHandle = this.getActivityHandle(); if (wrappedDialog.isServer()) { // FIXME: do nothing // action.setAction(DialogClientForkAction.DO_NOTHING); return false; } switch (this.forkState) { case AWAIT_FIRST_TAG: if (100 <= statusCode && statusCode < 200) { if (toTag != null) { this.localToTag = toTag; this.forkState = DialogForkState.AWAIT_FINAL; this.toTag2DialogHandle.put(this.localToTag, this.sipActivityHandle); this.fetchData(response); this.ra.addClientDialogMaping(this.wrappedDialog.getLocalTag() + "_" + this.wrappedDialog.getCallId().getCallId(), getActivityHandle()); } if (tracer.isFineEnabled()) { tracer.fine("Received 1xx message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } // FIXME: fire on this, original message firedByDialogWrapper = false; } else if (statusCode < 300) { this.forkState = DialogForkState.END; // This one is the master, here we can have only one dialog, // this one, so we dont care about simblings, we just dont have // those // baranowb: what should we do if no toTag? if(toTag!=null) { //THis is for cancel OK, which might not have tag. this.localToTag = toTag; this.toTag2DialogHandle.put(toTag, this.sipActivityHandle); } this.fetchData(response); if (tracer.isFineEnabled()) { tracer.fine("Received 2xx message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } // FIXME: fire on this, original message firedByDialogWrapper = false; // just in case terminateFork(masterActivityHandle); } else if (statusCode < 700) { this.forkState = DialogForkState.END; if (tracer.isFineEnabled()) { tracer.fine("Received failure message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } // FIXME: fire on this, original message FireableEventType eventID = this.ra.eventIdCache.getEventId(this.ra.getRaContext().getEventLookupFacility(), response); ResponseEventWrapper REW = new ResponseEventWrapper(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, response); this.ra.fireEvent(REW, this.getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)).getAddress() .toString()),false); firedByDialogWrapper = true; terminateFork(null); } else { if (tracer.isFineEnabled()) { tracer.fine("Received strange message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } } break; case AWAIT_FINAL: if (100 <= statusCode && statusCode < 200) { if (tracer.isFineEnabled()) { tracer.fine("Received 1xx message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } if (toTag == null) { // FIXME: fire on this firedByDialogWrapper = false; } else if (toTag.compareTo(localToTag)==0) { this.fetchData(response); firedByDialogWrapper = false; // FIXME: fire on this } else if (this.toTag2DialogHandle.containsKey(toTag)) { // other dialog wins DialogWrapper child = (DialogWrapper) this.ra.getActivity(this.toTag2DialogHandle.get(toTag)); if (child == null) { this.toTag2DialogHandle.remove(toTag); return true; } child.fetchData(response); // FIXME: fire on child, original message FireableEventType eventID = this.ra.eventIdCache.getEventId(this.ra.getRaContext().getEventLookupFacility(), response); if (tracer.isFineEnabled()) { tracer.fine("Received 1xx message: " + statusCode + ". ToTag:" + toTag + "EventId:" + eventID); } ResponseEventWrapper REW = new ResponseEventWrapper(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, response); this.ra.fireEvent(REW, child.getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)) .getAddress().toString()),false); firedByDialogWrapper = true; } else { // We create fake dialog that - as a child of master DialogWrapper child = this.provider.getNewDialogActivity(wrappedDialog, this.getActivityHandle(), null); child.forkState = this.forkState; child.localToTag = toTag; child.fetchData(response); this.toTag2DialogHandle.put(toTag, child.getActivityHandle()); // FIXME: fire on original, dialog forked FireableEventType eventID = this.ra.eventIdCache.getDialogForkEventId(this.ra.getRaContext().getEventLookupFacility()); DialogForkedEvent REW = new DialogForkedEvent(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, child, response); this.ra.fireEvent(REW, this.getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)) .getAddress().toString()),false); firedByDialogWrapper = true; } } else if (statusCode < 300) { this.forkState = DialogForkState.END; if (tracer.isFineEnabled()) { tracer.fine("Received 2xx message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } //toTag must not be null, but just in case. if (this.localToTag != null && toTag!=null && this.localToTag.compareTo(toTag)==0) { // we win this.fetchData(response); // FIXME: fire on original, original message firedByDialogWrapper = false; } else if (toTag!=null && this.toTag2DialogHandle.containsKey(toTag)) { // other dialog wins DialogWrapper child = (DialogWrapper) this.ra.getActivity(this.toTag2DialogHandle.get(toTag)); child.fetchData(response); masterActivityHandle = child.getActivityHandle(); child.makeMaster(); if (child != this) this.forkInitialActivityHandle = child.getActivityHandle(); else { tracer.severe("Local: " + localToTag + " : " + toTag + " MSG:\n" + response); } // FIXME: fire on child, original message FireableEventType eventID = this.ra.eventIdCache.getEventId(this.ra.getRaContext().getEventLookupFacility(), response); ResponseEventWrapper REW = new ResponseEventWrapper(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, response); this.ra.fireEvent(REW, child.getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)) .getAddress().toString()),false); firedByDialogWrapper = true; } else if(toTag !=null){ // we have completly new master dialog // We create fake dialog as a child of master, but this one // becomes master DialogWrapper child = this.provider.getNewDialogActivity(wrappedDialog, this.getActivityHandle(), null); child.forkState = this.forkState; child.localToTag = toTag; this.toTag2DialogHandle.put(toTag, child.getActivityHandle()); masterActivityHandle = child.getActivityHandle(); child.makeMaster(); this.forkInitialActivityHandle = child.getActivityHandle(); child.fetchData(response); // FIXME: fire on child, original message FireableEventType eventID = this.ra.eventIdCache.getDialogForkEventId(this.ra.getRaContext().getEventLookupFacility()); DialogForkedEvent REW = new DialogForkedEvent(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, child, response); this.ra.fireEvent(REW, this.getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)) .getAddress().toString()),false); firedByDialogWrapper = true; }else { tracer.severe("Received 2xx reponse without toTag, this is error."); } // just in case terminateFork(masterActivityHandle); } else if (statusCode < 700) { this.forkState = DialogForkState.END; if (tracer.isFineEnabled()) { tracer.fine("Received failure message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } // FIXME: fire on this, original message FireableEventType eventID = this.ra.eventIdCache.getEventId(this.ra.getRaContext().getEventLookupFacility(), response); ResponseEventWrapper REW = new ResponseEventWrapper(this.provider, (ClientTransaction) respEvent.getClientTransaction().getApplicationData(), this, response); this.ra.fireEvent(REW, getActivityHandle(), eventID, new javax.slee.Address(AddressPlan.SIP, ((FromHeader) response.getHeader(FromHeader.NAME)).getAddress() .toString()),false); firedByDialogWrapper = true; terminateFork(null); } else { if (tracer.isFineEnabled()) { tracer.fine("Received strange message: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } } break; case END: // Strictly for ending other dialogs - response may come late // FIXME: add terminate dialog if (toTag!=null && !toTag.equals(wrappedDialog.getRemoteTag())) { // if 1xx+, 3xx+ we ignore it - as it indicates error response to another dialog, we dont care for. if (statusCode < 200 || statusCode > 300) { if (tracer.isFineEnabled()) { tracer.fine("Received late message, action IGNORE: " + statusCode + ". ToTag:" + toTag + ". Fork state old:" + oldForkState + " - new" + this.forkState + ". On dialog: " + this.toString()); } } else { // we are in 2xx zone, we have to ack and send bye // FIXME: Add proper termiantion for SUBSCRIBE/REFER ??? if (dialogCreatingMethods.contains(((CSeqHeader) response.getHeader(CSeqHeader.NAME)).getMethod())) doTerminateOnLate2xx(respEvent); } firedByDialogWrapper = true; } // else we leave it to be fired by normal means ? break; } return firedByDialogWrapper; } /** * Generare 200 and sends BYE if method == 200, possibly this shoudl also terminate subscriptions? * @param respEvent */ public void doTerminateOnLate2xx(ResponseEvent respEvent) { try { Response response = respEvent.getResponse(); CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); List<Header> routeSet = getRouteList(response); SipURI requestURI = getReqeustUri(response); long cseqNumber = cseq.getSeqNumber(); long statusCode = response.getStatusCode(); // logger.info("DOING FORGE FOR: \n"+response); if (cseq.getMethod().equals(Request.INVITE) && (statusCode < 300 && statusCode >= 200)) { if (requestURI == null) { tracer.severe("Cannot ack on reqeust that has empty contact!!!!"); return; } MaxForwardsHeader mf = this.provider.getHeaderFactory().createMaxForwardsHeader(70); Request forgedRequest = this.provider.getMessageFactory().createRequest(null); forgedRequest.setRequestURI(requestURI); forgedRequest.addHeader(mf); forgedRequest.addHeader(response.getHeader(CallIdHeader.NAME)); forgedRequest.addHeader(this.provider.getHeaderFactory().createCSeqHeader(cseqNumber, Request.ACK)); forgedRequest.addHeader(response.getHeader(FromHeader.NAME)); forgedRequest.addHeader(response.getHeader(ToHeader.NAME)); for (Header h : routeSet) { forgedRequest.addLast(h); } forgedRequest.addHeader(this.provider.getLocalVia(this.provider.getListeningPoints()[0].getTransport(), null)); // ITS BUG.... ((SIPRequest) forgedRequest).setMethod(Request.ACK); this.provider.sendRequest(forgedRequest); forgedRequest = this.provider.getMessageFactory().createRequest(null); forgedRequest.setRequestURI(requestURI); forgedRequest.addHeader(mf); forgedRequest.addHeader(response.getHeader(CallIdHeader.NAME)); forgedRequest.addHeader(this.provider.getHeaderFactory().createCSeqHeader(cseqNumber + 1, Request.BYE)); forgedRequest.addHeader(response.getHeader(FromHeader.NAME)); forgedRequest.addHeader(response.getHeader(ToHeader.NAME)); for (Header h : routeSet) { forgedRequest.addLast(h); } forgedRequest.addHeader(this.provider.getLocalVia(this.provider.getListeningPoints()[0].getTransport(), null)); // ITS BUG.... ((SIPRequest) forgedRequest).setMethod(Request.BYE); this.provider.sendRequest(forgedRequest); } else { // FIXME: add sometihng here? } // response.get } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Forges reqeust URI using contact and To name par tof address URI, this is * requries since on fork this is how target is determined * * @param response * @return */ private SipURI getReqeustUri(Response response) { ContactHeader contact = ((ContactHeader) response.getHeader(ContactHeader.NAME)); if (contact != null) { SipURI contactURI = (SipURI) contact.getAddress().getURI(); SipURI requestURI; try { requestURI = this.provider.getAddressFactory().createSipURI(contactURI.getUser(), contactURI.getHost()); requestURI.setPort(contactURI.getPort()); return requestURI; } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { } return null; } /** * Generates route list the same way dialog does. * @param response * @return */ private List getRouteList(Response response) { // we have record route set, as we are client, this is reversed ArrayList<RouteHeader> routeList = new ArrayList<RouteHeader>(); ListIterator rrLit = response.getHeaders(RecordRouteHeader.NAME); while (rrLit.hasNext()) { RecordRouteHeader rrh = (RecordRouteHeader) rrLit.next(); RouteHeader rh = this.provider.getHeaderFactory().createRouteHeader(rrh.getAddress()); Iterator pIt = rrh.getParameterNames(); while (pIt.hasNext()) { String pName = (String) pIt.next(); try { rh.setParameter(pName, rrh.getParameter(pName)); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } routeList.add(0, rh); } return routeList; } private void fetchData(Response response) { // fetch routeSet, requestURI and ToTag, callId // this is done only once? if (localToTag == null) { // all of those will become solid after tag is set , but will be // present without it. localToTag = ((ToHeader) response.getHeader(ToHeader.NAME)).getTag(); reqeustURI = null; this.localRouteSet.clear(); } ContactHeader contact = ((ContactHeader) response.getHeader(ContactHeader.NAME)); //issue 623 if(contact!=null) if (reqeustURI == null || (contact != null && !reqeustURI.equals(contact.getAddress().getURI()))) { reqeustURI = (SipURI) contact.getAddress().getURI(); } //FIXME: is this correct? if (localRouteSet.size() == 0 || this.routeSetOnRequest) { this.routeSetOnRequest = false; this.localRouteSet.clear(); this.localRouteSet.addAll(getRouteList(response)); } this.setCallIdToReUse((CallIdHeader) response.getHeader(CallIdHeader.NAME)); } public void generateRouteList(Request origRequest) { //FIXME: is this the only case ? if(this.wrappedDialog == null) { ViaHeader topVia = (ViaHeader) origRequest.getHeader(ViaHeader.NAME); Address address =null; if(topVia!=null) { address = getLocalAddressForTransport(topVia.getTransport()); }else { if(tracer.isFineEnabled()) { tracer.fine("There is no via header, can not compute AS contact. Skipping computation of request route."); } return; } ListIterator lit=origRequest.getHeaders(RouteHeader.NAME); if(lit!=null ) { GenericURI addressURI = (GenericURI) (address == null? null: address.getURI()); //we atleast have one header //FIXME: possibly we have to do here some proxy work: RFC 3261 16.4 Route Information Preprocessing //FIXME: add check to remove first if it points to us. while(lit.hasNext()) { RouteHeader rh=(RouteHeader) lit.next(); GenericURI rhURI = (GenericURI) rh.getAddress().getURI(); if(addressURI!=null && (rhURI.equals(addressURI))) {} else { this.localRouteSet.add(rh); } } } } } private Address getLocalAddressForTransport(String transport) { ListeningPoint lp = this.provider.getListeningPoint(transport); Address address = null;; if(lp!=null) { try { address = this.provider.getAddressFactory().createAddress("Mobicents SIP AS <sip:" + lp.getIPAddress() + ">"); ((SipURI) address.getURI()).setPort(lp.getPort()); } catch (ParseException e) { e.printStackTrace(); //throw new SipException("Failed to create contact header.",e); } }else { if(tracer.isFineEnabled()) { tracer.fine("There is no listening point, for transport: "+transport+", can not compute AS contact."); } } return address; } private void terminateFork(SipActivityHandle masterToRetain) { // it ConcurrentMod shouldnt happen but... HashSet<String> tagSet = new HashSet<String>(toTag2DialogHandle.keySet()); Iterator<String> tagIterator = tagSet.iterator(); while (tagIterator.hasNext()) { String tag = tagIterator.next(); if (masterToRetain != null && toTag2DialogHandle.get(tag) == masterToRetain) { // tagSet.remove(tag); tagIterator.remove(); continue; } DialogWrapper dw = (DialogWrapper) this.ra.getActivity(toTag2DialogHandle.get(tag)); if (dw == null) { toTag2DialogHandle.remove(tag); tagIterator.remove(); } else { dw.delete(); } } // toTag2DialogHandle.keySet().removeAll(tagSet); } private void makeMaster() { this.forkState = DialogForkState.END; this.forkInitialActivityHandle = null; this.wrappedDialog.setApplicationData(this); this.ra.addClientDialogMaping(this.wrappedDialog.getLocalTag() + "_" + this.wrappedDialog.getCallId().getCallId(), getActivityHandle()); } protected boolean isInForkedActions() { return this.forkInitialActivityHandle!=null||this.forkState==DialogForkState.AWAIT_FINAL; } }