/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processTimeout(TimeoutEvent event) { try { Transaction transaction; if (event.isServerTransaction()) { transaction = event.getServerTransaction(); } else { transaction = event.getClientTransaction(); } ProtocolProviderServiceSipImpl recipient = getServiceData(transaction); if (recipient == null) { logger.error( "We received a timeout which wasn't " + "marked, please report this to " + "*****@*****.**"); } else { recipient.processTimeout(event); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** The real task work, replace listening point. */ public void run() { // if the provider is still unregistering it most probably won't // successes until we re-init the LP if (protocolProvider.getRegistrationState() == RegistrationState.UNREGISTERING) { String transport = protocolProvider.getRegistrarConnection().getTransport(); ListeningPoint old = getLP(transport); try { stack.deleteListeningPoint(old); } catch (Throwable t) { logger.warn("Error replacing ListeningPoint for " + transport, t); } try { ListeningPoint tcpLP = stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, transport.equals(ListeningPoint.TCP) ? getPreferredClearPort() : getPreferredSecurePort(), transport); clearJainSipProvider.addListeningPoint(tcpLP); } catch (Throwable t) { logger.warn( "Error replacing ListeningPoint for " + protocolProvider.getRegistrarConnection().getTransport(), t); } } resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport()); }
/** * Creates a new instance of this class using the specified parent <tt>protocolProvider</tt>. * * @param protocolProvider a reference to the <tt>ProtocolProviderServiceSipImpl</tt> instance * that created us. */ public ClientCapabilities(ProtocolProviderServiceSipImpl protocolProvider) { this.provider = protocolProvider; provider.registerMethodProcessor(Request.OPTIONS, this); registrationListener = new RegistrationListener(); provider.addRegistrationStateChangeListener(registrationListener); }
/** * Notified when registration state changed for a provider. * * @param evt */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { if (evt.getNewState() == RegistrationState.UNREGISTERING) { new Timer().schedule(this, TIME_FOR_PP_TO_UNREGISTER); } else { protocolProvider.removeRegistrationStateChangeListener(this); resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport()); } }
/** Fire event that connection has failed and we had to unregister the protocol provider. */ private void disconnect() { // don't alert the user if we're already off if (provider.getRegistrationState().equals(RegistrationState.UNREGISTERED)) { return; } provider .getRegistrarConnection() .setRegistrationState( RegistrationState.CONNECTION_FAILED, RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, "A timeout occurred while trying to connect to the server."); }
/** * Creates an instance of this operation set. * * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> that created us and that * we'll use for retrieving the underlying aim connection. */ OperationSetBasicInstantMessagingSipImpl(ProtocolProviderServiceSipImpl provider) { this.sipProvider = provider; provider.addRegistrationStateChangeListener(new RegistrationStateListener()); offlineMessageSupported = provider.getAccountID().getAccountPropertyBoolean("OFFLINE_MSG_SUPPORTED", false); sipProvider.registerMethodProcessor( Request.MESSAGE, new BasicInstantMessagingMethodProcessor()); this.sipStatusEnum = sipProvider.getSipStatusEnum(); }
/** * Receives options requests and replies with an OK response containing methods that we support. * * @param requestEvent the incoming options request. * @return <tt>true</tt> if request has been successfully processed, <tt>false</tt> otherwise */ @Override public boolean processRequest(RequestEvent requestEvent) { Response optionsOK = null; try { optionsOK = provider.getMessageFactory().createResponse(Response.OK, requestEvent.getRequest()); // add to the allows header all methods that we support for (String method : provider.getSupportedMethods()) { // don't support REGISTERs if (!method.equals(Request.REGISTER)) optionsOK.addHeader(provider.getHeaderFactory().createAllowHeader(method)); } Iterable<String> knownEventsList = provider.getKnownEventsList(); synchronized (knownEventsList) { for (String event : knownEventsList) optionsOK.addHeader(provider.getHeaderFactory().createAllowEventsHeader(event)); } } catch (ParseException ex) { // What else could we do apart from logging? logger.warn("Failed to create an incoming OPTIONS request", ex); return false; } try { SipStackSharing.getOrCreateServerTransaction(requestEvent).sendResponse(optionsOK); } catch (TransactionUnavailableException ex) { // this means that we received an OPTIONS request outside the scope // of a transaction which could mean that someone is simply sending // us b****hit to keep a NAT connection alive, so let's not get too // excited. if (logger.isInfoEnabled()) logger.info("Failed to respond to an incoming " + "transactionless OPTIONS request"); if (logger.isTraceEnabled()) logger.trace("Exception was:", ex); return false; } catch (InvalidArgumentException ex) { // What else could we do apart from logging? logger.warn("Failed to send an incoming OPTIONS request", ex); return false; } catch (SipException ex) { // What else could we do apart from logging? logger.warn("Failed to send an incoming OPTIONS request", ex); return false; } return true; }
/** * Removes from the specified list of candidates providers connected to a registrar that does not * match the IP address that we are receiving a request from. * * @param candidates the list of providers we've like to filter. * @param request the request that we are currently dispatching */ private void filterByAddress(List<ProtocolProviderServiceSipImpl> candidates, Request request) { Iterator<ProtocolProviderServiceSipImpl> iterPP = candidates.iterator(); while (iterPP.hasNext()) { ProtocolProviderServiceSipImpl candidate = iterPP.next(); if (candidate.getRegistrarConnection() == null) { // RegistrarLess connections are ok continue; } if (!candidate.getRegistrarConnection().isRegistrarless() && !candidate.getRegistrarConnection().isRequestFromSameConnection(request)) { iterPP.remove(); } } }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processResponse(ResponseEvent event) { try { // we don't have to accept the transaction since we // created the request ClientTransaction transaction = event.getClientTransaction(); if (logger.isTraceEnabled()) logger.trace( "received response: " + event.getResponse().getStatusCode() + " " + event.getResponse().getReasonPhrase()); if (transaction == null) { logger.warn("Transaction is null, probably already expired!"); return; } ProtocolProviderServiceSipImpl service = getServiceData(transaction); if (service != null) { // Mark the dialog for the dispatching of later in-dialog // responses. If there is no dialog then the initial request // sure is marked otherwise we won't have found the service with // getServiceData(). The request has to be marked in case we // receive one more response in an out-of-dialog transaction. if (event.getDialog() != null) { SipApplicationData.setApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE, service); } service.processResponse(event); } else { logger.error( "We received a response which " + "wasn't marked, please report this to " + "*****@*****.**"); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processDialogTerminated(DialogTerminatedEvent event) { try { ProtocolProviderServiceSipImpl recipient = (ProtocolProviderServiceSipImpl) SipApplicationData.getApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE); if (recipient == null) { logger.error( "Dialog wasn't marked, please report this to " + "*****@*****.**"); } else { if (logger.isTraceEnabled()) logger.trace("service was found with dialog data"); recipient.processDialogTerminated(event); } } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Listens for network changes and if we have a down interface and we have a tcp/tls provider * which is staying for 20 seconds in unregistering state, it cannot unregister cause its using * the old address which is currently down, and we must recreate its listening points so it can * further reconnect. * * @param event the change event. */ public void configurationChanged(ChangeEvent event) { if (event.isInitial()) return; if (event.getType() == ChangeEvent.ADDRESS_DOWN) { for (final ProtocolProviderServiceSipImpl pp : listeners) { if (pp.getRegistrarConnection().getTransport() != null && (pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TCP) || pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TLS))) { ResetListeningPoint reseter; synchronized (resetListeningPointsTimers) { // we do this only once for transport if (resetListeningPointsTimers.containsKey(pp.getRegistrarConnection().getTransport())) continue; reseter = new ResetListeningPoint(pp); resetListeningPointsTimers.put(pp.getRegistrarConnection().getTransport(), reseter); } pp.addRegistrationStateChangeListener(reseter); } } } }
/** Frees allocated resources. */ void shutdown() { provider.removeRegistrationStateChangeListener(registrationListener); }
/** * Construct a <tt>Request</tt> represent a new message. * * @param to the <tt>Contact</tt> to send <tt>message</tt> to * @param message the <tt>Message</tt> to send. * @return a Message Request destined to the contact * @throws OperationFailedException if an error occurred during the creation of the request */ Request createMessageRequest(Contact to, Message message) throws OperationFailedException { Address toAddress = null; try { toAddress = sipProvider.parseAddressString(to.getAddress()); } catch (ParseException exc) { // Shouldn't happen logger.error("An unexpected error occurred while" + "constructing the address", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the address", OperationFailedException.INTERNAL_ERROR, exc); } // Call ID CallIdHeader callIdHeader = this.sipProvider.getDefaultJainSipProvider().getNewCallId(); // CSeq CSeqHeader cSeqHeader = null; try { // protect seqN synchronized (this) { cSeqHeader = this.sipProvider.getHeaderFactory().createCSeqHeader(seqN++, Request.MESSAGE); } } catch (InvalidArgumentException ex) { // Shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeadder", OperationFailedException.INTERNAL_ERROR, ex); } catch (ParseException exc) { // shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeadder", OperationFailedException.INTERNAL_ERROR, exc); } // FromHeader and ToHeader String localTag = SipMessageFactory.generateLocalTag(); FromHeader fromHeader = null; ToHeader toHeader = null; try { // FromHeader fromHeader = this.sipProvider .getHeaderFactory() .createFromHeader(sipProvider.getOurSipAddress(toAddress), localTag); // ToHeader toHeader = this.sipProvider.getHeaderFactory().createToHeader(toAddress, null); } catch (ParseException ex) { // these two should never happen. logger.error( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", OperationFailedException.INTERNAL_ERROR, ex); } // ViaHeaders ArrayList<ViaHeader> viaHeaders = this.sipProvider.getLocalViaHeaders(toAddress); // MaxForwards MaxForwardsHeader maxForwards = this.sipProvider.getMaxForwardsHeader(); // Content params ContentTypeHeader contTypeHeader; ContentLengthHeader contLengthHeader; try { contTypeHeader = this.sipProvider .getHeaderFactory() .createContentTypeHeader(getType(message), getSubType(message)); if (!DEFAULT_MIME_ENCODING.equalsIgnoreCase(message.getEncoding())) contTypeHeader.setParameter("charset", message.getEncoding()); contLengthHeader = this.sipProvider.getHeaderFactory().createContentLengthHeader(message.getSize()); } catch (ParseException ex) { // these two should never happen. logger.error("An unexpected error occurred while" + "constructing the content headers", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the content headers", OperationFailedException.INTERNAL_ERROR, ex); } catch (InvalidArgumentException exc) { // these two should never happen. logger.error( "An unexpected error occurred while" + "constructing the content length header", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the content length header", OperationFailedException.INTERNAL_ERROR, exc); } Request req; try { req = this.sipProvider .getMessageFactory() .createRequest( toHeader.getAddress().getURI(), Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards, contTypeHeader, message.getRawData()); } catch (ParseException ex) { // shouldn't happen logger.error("Failed to create message Request!", ex); throw new OperationFailedException( "Failed to create message Request!", OperationFailedException.INTERNAL_ERROR, ex); } req.addHeader(contLengthHeader); return req; }
/** * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners) * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the * request URI, and then at the To field to find a matching candidate for dispatching. Note that * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>. * * @param request the <tt>Request</tt> to find a recipient for. * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>. */ private ProtocolProviderServiceSipImpl findTargetFor(Request request) { if (request == null) { logger.error("request shouldn't be null."); return null; } List<ProtocolProviderServiceSipImpl> currentListenersCopy = new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners()); // Let's first narrow down candidate choice by comparing // addresses and ports (no point in delivering to a provider with a // non matching IP address since they will reject it anyway). filterByAddress(currentListenersCopy, request); if (currentListenersCopy.size() == 0) { logger.error("no listeners"); return null; } URI requestURI = request.getRequestURI(); if (requestURI.isSipURI()) { String requestUser = ((SipURI) requestURI).getUser(); List<ProtocolProviderServiceSipImpl> candidates = new ArrayList<ProtocolProviderServiceSipImpl>(); // check if the Request-URI username is // one of ours usernames for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) { String ourUserID = listener.getAccountID().getUserID(); // logger.trace(ourUserID + " *** " + requestUser); if (ourUserID.equals(requestUser)) { if (logger.isTraceEnabled()) logger.trace("suitable candidate found: " + listener.getAccountID()); candidates.add(listener); } } // the perfect match // every other case is approximation if (candidates.size() == 1) { ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0); if (logger.isTraceEnabled()) logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\""); return perfectMatch; } // more than one account match if (candidates.size() > 1) { // check if a custom param exists in the contact // address (set for registrar accounts) for (ProtocolProviderServiceSipImpl candidate : candidates) { String hostValue = ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME); if (hostValue == null) continue; if (hostValue.equals(candidate.getContactAddressCustomParamValue())) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "\" the custom param was set"); return candidate; } } // Past this point, our guess is not reliable. We try to find // the "least worst" match based on parameters like the To field // check if the To header field host part // matches any of our SIP hosts for (ProtocolProviderServiceSipImpl candidate : candidates) { URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI(); if (fromURI.isSipURI() == false) continue; SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI(); String ourHost = ourURI.getHost(); URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI(); if (toURI.isSipURI() == false) continue; String toHost = ((SipURI) toURI).getHost(); // logger.trace(toHost + "***" + ourHost); if (toHost.equals(ourHost)) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "host in the To: is the same as in our AOR"); return candidate; } } // fallback on the first candidate ProtocolProviderServiceSipImpl target = candidates.iterator().next(); logger.info( "Will randomly dispatch to \"" + target.getAccountID() + "\" because there is ambiguity on the username from" + " the Request-URI"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } // fallback on any account ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next(); if (logger.isDebugEnabled()) logger.debug( "Will randomly dispatch to \"" + target.getAccountID() + "\" because the username in the Request-URI " + "is unknown or empty"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } else { logger.error("Request-URI is not a SIP URI, dropping"); } return null; }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processRequest(RequestEvent event) { try { Request request = event.getRequest(); if (logger.isTraceEnabled()) logger.trace("received request: " + request.getMethod()); /* * Create the transaction if it doesn't exist yet. If it is a * dialog-creating request, the dialog will also be automatically * created by the stack. */ if (event.getServerTransaction() == null) { try { // apply some hacks if needed on incoming request // to be compliant with some servers/clients // if needed stop further processing. if (applyNonConformanceHacks(event)) return; SipProvider source = (SipProvider) event.getSource(); ServerTransaction transaction = source.getNewServerTransaction(request); /* * Update the event, otherwise getServerTransaction() and * getDialog() will still return their previous value. */ event = new RequestEvent(source, transaction, transaction.getDialog(), request); } catch (SipException ex) { logger.error( "couldn't create transaction, please report " + "this to [email protected]", ex); } } ProtocolProviderServiceSipImpl service = getServiceData(event.getServerTransaction()); if (service != null) { service.processRequest(event); } else { service = findTargetFor(request); if (service == null) { logger.error("couldn't find a ProtocolProviderServiceSipImpl " + "to dispatch to"); if (event.getServerTransaction() != null) event.getServerTransaction().terminate(); } else { /* * Mark the dialog for the dispatching of later in-dialog * requests. If there is no dialog, we need to mark the * request to dispatch a possible timeout when sending the * response. */ Object container = event.getDialog(); if (container == null) container = request; SipApplicationData.setApplicationData(container, SipApplicationData.KEY_SERVICE, service); service.processRequest(event); } } } catch (Throwable exc) { /* * Any exception thrown within our code should be caught here so * that we could log it rather than interrupt stack activity with * it. */ this.logApplicationException(DialogTerminatedEvent.class, exc); // Unfortunately, death can hardly be ignored. if (exc instanceof ThreadDeath) throw (ThreadDeath) exc; } }