/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in // * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * Large portions of this software are based upon public domain software * https://sip-communicator.dev.java.net/ * *//* * SipProvider.java * * Created on November 18, 2003, 2:18 PM */ package net.sourceforge.gjtapi.raw.sipprovider; import java.io.IOException; import java.util.Collection; import java.util.Dictionary; import java.util.Iterator; import java.util.Map; import java.util.Properties; import javax.media.IncompatibleSourceException; import javax.telephony.InvalidArgumentException; import javax.telephony.InvalidPartyException; import javax.telephony.MethodNotSupportedException; import javax.telephony.PrivilegeViolationException; import javax.telephony.ProviderUnavailableException; import javax.telephony.ResourceUnavailableException; import net.sourceforge.gjtapi.CallId; import net.sourceforge.gjtapi.RawSigDetectEvent; import net.sourceforge.gjtapi.RawStateException; import net.sourceforge.gjtapi.ResourceConfigurable; import net.sourceforge.gjtapi.ResourceFinder; import net.sourceforge.gjtapi.TelephonyListener; import net.sourceforge.gjtapi.TermData; import net.sourceforge.gjtapi.raw.MediaTpi; import net.sourceforge.gjtapi.raw.PrivateDataTpi; import net.sourceforge.gjtapi.raw.sipprovider.common.Console; import net.sourceforge.gjtapi.raw.sipprovider.common.NetworkAddressManager; import net.sourceforge.gjtapi.raw.sipprovider.sip.SipManager; /** * This is a provider that hooks into the SIP Communicator to provided Session * Initiation Protocol support for GJTAPI */ public class SipProvider implements MediaTpi, PrivateDataTpi, ResourceConfigurable { // Used by sendPrivateData to allow a client to get a MediaSource for a // terminal. public static final String GET_MEDIA_SOURCE = "GetMediaSource"; private TelephonyListener listener; // private List addresses; // private TermData terminal; // private final static String RESOURCE_NAME = "sip.props"; /** Logger instance. */ protected static Console console = Console.getConsole(SipProvider.class); // private CallId idd; /** Known sip phones. */ private final Collection<SipPhone> sipPhones = new java.util.ArrayList<SipPhone>(); /** The address manager. */ private NetworkAddressManager addressManager; /** * Constructs a new object. */ public SipProvider() { } /** * Add an observer for RawEvents * * @param ro * New event listener * @return void * */ public void addListener(TelephonyListener ro) { console.logEntry(); if (listener == null) { listener = ro; } else { System.err.println("Request to add a TelephonyListener to " + this.getClass().getName() + ", but one is already registered"); } } /** * Answer a call that has appeared at a particular terminal * * @param call * The system identifier for the call * @param address * The address of the connection that is ringing. * @param terminal * the terminal to answer the call on. * @exception RawStateException * A holder for a low-level state problem. * */ public void answerCall(CallId call, String address, String terminal) throws PrivilegeViolationException, ResourceUnavailableException, MethodNotSupportedException, RawStateException { console.logEntry(); SipPhone mySP = getSipPhoneByAddress(address); mySP.answerCall(call, address, terminal); } /** * Tell the provider to reserve a call id for future use. The provider does * not have to hang onto it. Creation date: (2000-02-16 14:48:48) * * @author: Richard Deadman * @return The CallId created by the provider. * @param term * The address that the call will start on. Used by muxes to * isolate the correct provider. * @exception InvalidArgumentException * If the Address is not in the provider's domain. * */ // prepare un jtapi CallId public CallId reserveCallId(String address) throws InvalidArgumentException { return new SipCallId(); } /** * Make a call. Not that this follows the JTAPI semantics of an idle call * being created synchronously (two connections). Events from the raw * provider will indicate state transitions. * * @param id * The callId reserved for the call. * @param address * The logical address to make a call from * @param term * The physical address for the call, if applicable * @param dest * The destination address * @return A call Id. This may be used later to track call progress. * @exception RawStateException * One of the objects is not in the correct state. * */ public CallId createCall(CallId id, String address, String term, String dest) throws ResourceUnavailableException, PrivilegeViolationException, InvalidPartyException, InvalidArgumentException, RawStateException, MethodNotSupportedException { console.logEntry(); if (console.isDebugEnabled()) { console.debug("id = " + id); console.debug("trentative de connection a " + address); } // CREATION D'UN CALL (SIP) final SipPhone mySP = getSipPhoneByAddress(address); mySP.createCall(id, address, term, dest); return id; } /** * Get a list of available addresses. This may be null if the Telephony * (raw) Provider does not support Addresses! If the Address set it too * large, this will throw a ResourceUnavailableException Creation date: * (2000-02-11 12:29:00) * * @author: Richard Deadman * @return An array of address names * @exception ResourceUnavailableException * if the set it too large to be returned dynamically. * */ public String[] getAddresses() throws ResourceUnavailableException { console.logEntry(); String[] ret = new String[sipPhones.size()]; Iterator<SipPhone> iter = sipPhones.iterator(); int i = 0; while (iter.hasNext()) { final SipPhone sp = iter.next(); ret[i] = sp.getAddress(); i++; } return ret; } /** * Get all the addresses associated with a terminal. Creation date: * (2000-06-02 12:30:54) * * @author: Richard Deadman * @return An array of address numbers. * @param terminal * The terminal name we want address numbers for. * @throws InvalidArgumentException * indicating that the terminal is unknown. * */ public String[] getAddresses(String terminal) throws InvalidArgumentException { console.logEntry(); Iterator<SipPhone> iter = sipPhones.iterator(); int size = 0; while (iter.hasNext()) { final SipPhone sp = iter.next(); SipManager sm = sp.getSipManager(); size++; } String[] ret = new String[size]; iter = sipPhones.iterator(); int i = 0; while (iter.hasNext()) { SipPhone sp = (SipPhone) iter.next(); SipManager sm = sp.getSipManager(); if (sm.getAddress().equals(terminal)) ; ret[i] = sp.getAddress(); i++; } return ret; } /** * Ask the raw provider to update the capabilities offered by the provider * This is expected to return a map of capability names to strings. If the * string starts with 't' or 'T', the capability is turned on, otherwise it * is turned off. Alternatively Boolean values can be used. If the value is * not found for a key, the default value is used. * <P> * To use this feature, the RawProvider needs to copy the * GenericCapabilities.props file and change any properties that are * supported differently. The the RawProvider could load the properties file * into a Properties object and return it. If the default value is * supported, then the corresponding line may be omitted from the file. * Creation date: (2000-03-14 14:48:36) * * @author: Richard Deadman * @return A properties file with name to value pairs for the basic raw * provider functions. * */ public java.util.Properties getCapabilities() { console.logEntry(); return null; } /** * Get a list of available terminals. This may be null if the Telephony * (raw) Provider does not support Terminals. If the Terminal set it too * large, this will throw a ResourceUnavailableException * <P> * Since we went to lazy connecting between Addresses and Terminals, this is * called so we don't have to follow all Address->Terminal associations to * get the full set of Terminals. Creation date: (2000-02-11 12:29:00) * * @author: Richard Deadman * @return An array of terminal names, media type containers. * @exception ResourceUnavailableException * if the set it too large to be returned dynamically. * */ public TermData[] getTerminals() throws ResourceUnavailableException { console.logEntry(); TermData[] ret = new TermData[sipPhones.size()]; Iterator<SipPhone> iter = sipPhones.iterator(); int i = 0; while (iter.hasNext()) { SipPhone sp = iter.next(); SipManager sm = sp.getSipManager(); ret[i] = new TermData(sm.getAddress(), true); ret[i] = new TermData(sp.getAddress(), true); i++; } return ret; } /** * Get all the terminals associated with an address. Creation date: * (2000-02-11 12:30:54) * * @author: Richard Deadman * @return An array of terminal name, media type containers. * @param address * The address number we want terminal names for. * @throws InvalidArgumentException * indicating that the address is unknown. * */ public TermData[] getTerminals(String address) throws InvalidArgumentException { Iterator<SipPhone> iter = sipPhones.iterator(); int size = 0; while (iter.hasNext()) { SipPhone sp = iter.next(); SipManager sm = sp.getSipManager(); if (sm.getAddress().equals(address)) ; size++; } TermData[] ret = new TermData[size]; iter = sipPhones.iterator(); int i = 0; while (iter.hasNext()) { SipPhone sp = (SipPhone) iter.next(); SipManager sm = sp.getSipManager(); if (sm.getAddress().equals(address)) ; ret[i] = new TermData(sp.getAddress(), true); i++; } return ret; } public void initialize(Map props) throws ProviderUnavailableException { addressManager = new NetworkAddressManager(); addressManager.init(props); } /** * {@inheritDoc} * */ public void initializeResources(Properties props, ResourceFinder finder) throws ProviderUnavailableException { String sipPhones = (String) props.get("gjtapi.sip.sip_phone"); if (sipPhones == null) { throw new ProviderUnavailableException("No phones defined!"); } String[] phones = sipPhones.split(","); for (String phone : phones) { try { addPhone(phone, finder); } catch (IOException e) { throw new ProviderUnavailableException(e.getMessage()); } } } /** * Adds the phone with the given configuration to the list of known * phones. * @param resource name of the properties resource. * @param finder utility to resolve resources * @throws IOException * Error reading the resource. */ public void addPhone(String resource, ResourceFinder finder) throws IOException { SipPhone phone = new SipPhone(resource, finder, this, addressManager); sipPhones.add(phone); console.debug("added phone " + phone.getAddress()); } /** * Release a connection to a call (Connection). This should block until the * connection is released. The service provider does not have to send a * disconnect message for this connection, since it is generated by the * framework. * * @param address * The address that we want to release * @param call * The call to disconnect from * * @exception RawException * Some low-level state exception occured. * */ public void release(String address, CallId call) throws PrivilegeViolationException, ResourceUnavailableException, MethodNotSupportedException, RawStateException { console.logEntry(); final SipPhone mySP = getSipPhoneByAddress(address); mySP.sipHangup(call); } /** * Tell the provider that it may release a call id for future use. This is * necessary to ensure that TelephonyProvider call ids are not reused until * the Generic JTAPI Framework layer is notified of the death of the call. * Creation date: (2000-02-17 22:25:48) * * @author: Richard Deadman * @param id * The CallId that may be freed. * */ public void releaseCallId(CallId id) { console.logEntry(); } /** * Remove a listener for RawEvents * * @param ro * Listener to remove * @return void * */ public void removeListener(TelephonyListener ro) { if (ro == listener) { listener = null; } else { System.err.println("Request to remove a TelephonyListener from " + this.getClass().getName() + ", but it wasn't registered"); } } /** * Perform any cleanup after my holder has finished with me. Creation date: * (2000-02-11 13:07:46) * * @author: Richard Deadman * */ public void shutdown() { console.logEntry(); } // methode utilitaires-------------------------------------- private SipPhone getSipPhoneByAddress(String address) { SipPhone ret = null; Iterator<SipPhone> iter = sipPhones.iterator(); while (iter.hasNext()) { SipPhone sp = iter.next(); if (sp.getAddress().equals(address)) { ret = sp; } } return ret; } // fin methodes utilitaires // methode de l'interphace SipPhoneListener public void sipCallActive(CallId id, int cause) { console.logEntry(); listener.callActive(id, cause); } public void sipConnectionInProgress(CallId id, String address, int cause) { console.logEntry(); listener.connectionInProgress(id, address, cause); } void sipConnectionAlerting(CallId id, String address, int cause) { console.logEntry(); listener.connectionAlerting(id, address, cause); } public void sipConnectionAuthorizeCallAttempt(CallId id, String address, int cause) { console.logEntry(); } public void sipConnectionCallDelivery(CallId id, String address, int cause) { console.logEntry(); } public void sipConnectionConnected(CallId id, String address, int cause) { console.logEntry(); listener.connectionConnected(id, address, cause); } public void sipConnectionDisconnected(CallId id, String address, int cause) { this.releaseCallId(id); listener.connectionDisconnected(id, address, cause); console.logEntry(); } public void sipConnectionFailed(CallId id, String address, int cause) { console.logEntry(); } public void sipTerminalConnectionCreated(CallId id, String address, String terminal, int cause) { console.logEntry(); listener.terminalConnectionCreated(id, address, terminal, cause); } public void sipTerminalConnectionRinging(CallId id, String address, String terminal, int cause) { console.logEntry(); listener.terminalConnectionRinging(id, address, terminal, cause); } // media // methods--------------------------------------------------------------------- // ---------------------------------------------------------------------------------- public boolean allocateMedia(String terminal, int type, Dictionary resourceArgs) { return true; } public boolean freeMedia(String terminal, int type) { return true; } public boolean isMediaTerminal(String terminal) { return true; } public void play(String terminal, String[] streamIds, int offset, javax.telephony.media.RTC[] rtcs, Dictionary optArgs) throws javax.telephony.media.MediaResourceException { console.logEntry(); try { final String[] add = this.getAddresses(terminal); for (int i = 0; i < add.length; i++) { final SipPhone sipPhone = this.getSipPhoneByAddress(add[i]); sipPhone.play(streamIds[0]); } } catch (javax.telephony.InvalidArgumentException ex) { console.debug(ex.toString()); } } public void record(String terminal, String streamId, javax.telephony.media.RTC[] rtcs, Dictionary optArgs) throws javax.telephony.media.MediaResourceException { console.logEntry(); try { final String[] add = this.getAddresses(terminal); for (int i = 0; i < add.length; i++) { final SipPhone sipPhone = this.getSipPhoneByAddress(add[i]); sipPhone.record(streamId); } } catch (javax.telephony.InvalidArgumentException ex) { console.debug(ex.toString()); } } /** * {@inheritDoc} */ public RawSigDetectEvent retrieveSignals(String terminal, int num, javax.telephony.media.Symbol[] patterns, javax.telephony.media.RTC[] rtcs, Dictionary optArgs) throws javax.telephony.media.MediaResourceException { return null; } public void sendSignals(String terminal, javax.telephony.media.Symbol[] syms, javax.telephony.media.RTC[] rtcs, Dictionary optArgs) throws javax.telephony.media.MediaResourceException { } public void stop(String terminal) { console.logEntry(); try { String[] add = this.getAddresses(terminal); for (int i = 0; i < add.length; i++) { SipPhone sipPhone = this.getSipPhoneByAddress(add[i]); sipPhone.stop(); } } catch (javax.telephony.InvalidArgumentException ex) { console.debug(ex.toString()); } } public void triggerRTC(String terminal, javax.telephony.media.Symbol action) { } public Object getPrivateData(CallId call, String address, String terminal) { // we don't support this return null; } /** * This is used to allow an application to retrieve JMF Media streams from * the provider so they can be used in real time for handling media */ public Object sendPrivateData(CallId call, String address, String terminal, Object data) { // we allow setting a media stream on a Terminal if ((terminal != null) && (data instanceof String) && (((String) data).equals(GET_MEDIA_SOURCE))) { // get the media stream try { // find the stream on the first successful phone String[] add = this.getAddresses(terminal); for (int i = 0; i < add.length; i++) { try { SipPhone sipPhone = this.getSipPhoneByAddress(add[i]); return sipPhone.mediaManager.getDataSource(); } catch (IncompatibleSourceException isEx) { // keep looping } catch (IOException ioEx) { // keep looping } } } catch (javax.telephony.InvalidArgumentException ex) { // couldn't get the addresses -- fall through and return null } } return null; } public void setPrivateData(CallId call, String address, String terminal, Object data) { // we don't support this } }