All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cybergarage.upnp.ControlPoint Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
/******************************************************************
*
*	CyberUPnP for Java
*
*	Copyright (C) Satoshi Konno 2002-2004
*
*	File: ControlPoint.java
*
*	Revision:
*
*	11/18/02
*		- first revision.
*	05/13/03
*		- Changed to create socket threads each local interfaces.
*		  (HTTP, SSDPNotiry, SSDPSerachResponse)
*	05/28/03
*		- Changed to send m-serach packets from SSDPSearchResponseSocket.
*		  The socket doesn't bind interface address.
*		- SSDPSearchResponsSocketList that binds a port and a interface can't
*		  send m-serch packets of IPv6 on J2SE v 1.4.1_02 and Redhat 9.
*	07/23/03
*		- Suzan Foster (suislief)
*		- Fixed a bug. HOST field was missing.
*	07/29/03
*		- Synchronized when a device is added by the ssdp message.
*	09/08/03
*		- Giordano Sassaroli 
*		- Problem : when an event notification message is received and the message
*		            contains updates on more than one variable, only the first variable update
*		            is notified.
*		- Error :  the other xml nodes of the message are ignored
*		- Fix : add two methods to the NotifyRequest for extracting the property array
*                and modify the httpRequestRecieved method in ControlPoint
*	12/12/03
*		- Added a static() to initialize UPnP class.
*	01/06/04
*		- Added the following methods to remove expired devices automatically
*		  removeExpiredDevices()
*		  setExpiredDeviceMonitoringInterval()/getExpiredDeviceMonitoringInterval()
*		  setDeviceDisposer()/getDeviceDisposer()
*	04/20/04
*		- Added the following methods.
*		  start(String target, int mx) and start(String target).
*	06/23/04
*		- Added setNMPRMode() and isNMPRMode().
*	07/08/04
*		- Added renewSubscriberService().
*		- Changed start() to create renew subscriber thread when the NMPR mode is true.
*	08/17/04
*		- Fixed removeExpiredDevices() to remove using the device array.
*	10/16/04
*		- Oliver Newell 
*		- Added this class to allow ControlPoint applications to be notified when 
*		  the ControlPoint base class adds/removes a UPnP device
*	03/30/05
*		- Changed addDevice() to use Parser::parse(URL).
*	04/12/06
*		- Added setUserData() and getUserData() to set a user original data object.
*
*******************************************************************/

package org.cybergarage.upnp;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;

import org.cybergarage.http.HTTPRequest;
import org.cybergarage.http.HTTPRequestListener;
import org.cybergarage.http.HTTPServerList;
import org.cybergarage.net.HostInterface;
import org.cybergarage.upnp.control.RenewSubscriber;
import org.cybergarage.upnp.device.DeviceChangeListener;
import org.cybergarage.upnp.device.Disposer;
import org.cybergarage.upnp.device.NotifyListener;
import org.cybergarage.upnp.device.ST;
import org.cybergarage.upnp.device.SearchResponseListener;
import org.cybergarage.upnp.device.USN;
import org.cybergarage.upnp.event.EventListener;
import org.cybergarage.upnp.event.NotifyRequest;
import org.cybergarage.upnp.event.Property;
import org.cybergarage.upnp.event.PropertyList;
import org.cybergarage.upnp.event.Subscription;
import org.cybergarage.upnp.event.SubscriptionRequest;
import org.cybergarage.upnp.event.SubscriptionResponse;
import org.cybergarage.upnp.ssdp.SSDP;
import org.cybergarage.upnp.ssdp.SSDPNotifySocketList;
import org.cybergarage.upnp.ssdp.SSDPPacket;
import org.cybergarage.upnp.ssdp.SSDPSearchRequest;
import org.cybergarage.upnp.ssdp.SSDPSearchResponseSocketList;
import org.cybergarage.util.Debug;
import org.cybergarage.util.ListenerList;
import org.cybergarage.util.Mutex;
import org.cybergarage.xml.Node;
import org.cybergarage.xml.NodeList;
import org.cybergarage.xml.Parser;
import org.cybergarage.xml.ParserException;

import net.i2p.util.Addresses;
import net.i2p.router.transport.TransportUtil;

public class ControlPoint implements HTTPRequestListener
{
	private final static int DEFAULT_EVENTSUB_PORT = 8058;
	private final static int DEFAULT_SSDP_PORT = 8008;
	private final static int DEFAULT_EXPIRED_DEVICE_MONITORING_INTERVAL = 60;
	
	private final static String DEFAULT_EVENTSUB_URI = "/evetSub";
	
	// I2P
	private static final boolean ALLOW_IPV6_LOCATION = true;

	////////////////////////////////////////////////
	//	Member
	////////////////////////////////////////////////
	
	private SSDPNotifySocketList ssdpNotifySocketList;
	private SSDPSearchResponseSocketList ssdpSearchResponseSocketList;

	/** I2P was private */
	protected SSDPNotifySocketList getSSDPNotifySocketList()
	{
		return ssdpNotifySocketList;
	}
	
	/** I2P was private */
	protected SSDPSearchResponseSocketList getSSDPSearchResponseSocketList()
	{
		return ssdpSearchResponseSocketList;
	}

	////////////////////////////////////////////////
	//	Initialize
	////////////////////////////////////////////////
	
	static 
	{
		UPnP.initialize();
	}
	
	////////////////////////////////////////////////
	//	Constructor
	////////////////////////////////////////////////

	public ControlPoint(int ssdpPort, int httpPort,InetAddress[] binds){
		ssdpNotifySocketList = new SSDPNotifySocketList(binds);
		ssdpSearchResponseSocketList = new SSDPSearchResponseSocketList(binds);
		
		setSSDPPort(ssdpPort);
		setHTTPPort(httpPort);
		
		setDeviceDisposer(null);
		setExpiredDeviceMonitoringInterval(DEFAULT_EXPIRED_DEVICE_MONITORING_INTERVAL);

		setRenewSubscriber(null);
				
		setNMPRMode(false);
		setRenewSubscriber(null);
	}
	
	public ControlPoint(int ssdpPort, int httpPort){
		this(ssdpPort,httpPort,null);
	}

	public ControlPoint()
	{
		this(DEFAULT_SSDP_PORT, DEFAULT_EVENTSUB_PORT);
	}

	public void finalize()
	{
		stop();
	}

	////////////////////////////////////////////////
	// Mutex
	////////////////////////////////////////////////
	
	private Mutex mutex = new Mutex();
	
	public void lock()
	{
		mutex.lock();
	}
	
	public void unlock()
	{
		mutex.unlock();
	}
	
	////////////////////////////////////////////////
	//	Port (SSDP)
	////////////////////////////////////////////////

	private int ssdpPort = 0;
	
	public int getSSDPPort() {
		return ssdpPort;
	}

	public void setSSDPPort(int port) {
		ssdpPort = port;
	}

	////////////////////////////////////////////////
	//	Port (EventSub)
	////////////////////////////////////////////////

	private int httpPort = 0;
	
	public int getHTTPPort() {
		return httpPort;
	}

	public void setHTTPPort(int port) {
		httpPort = port;
	}
	
	////////////////////////////////////////////////
	//	NMPR
	////////////////////////////////////////////////

	private boolean nmprMode;
	
	public void setNMPRMode(boolean flag)
	{
		nmprMode = flag;
	}

	public boolean isNMPRMode()
	{
		return nmprMode;
	}
	
	////////////////////////////////////////////////
	//	Device List
	////////////////////////////////////////////////

	private NodeList devNodeList = new NodeList();

	private void addDevice(Node rootNode)
	{
		devNodeList.add(rootNode);
	}

	private synchronized void addDevice(SSDPPacket ssdpPacket)
	{
		if (ssdpPacket.isRootDevice() == false)
			return;

		String usn = ssdpPacket.getUSN();

		String location = ssdpPacket.getLocation();
		try {	
			URL locationUrl = new URL(location);
			// I2P
			// Roku fake json port, the real UPnP port is 8060
			if (locationUrl.getPort() == 9080) {
				String lcusn = usn.toLowerCase(Locale.US);
				if (lcusn.contains("rku") || lcusn.contains("roku")) {
					Debug.warning("Ignoring Roku at " + location);
					return;
				}
			}
			// I2P
			// We duplicate all the checks in Parser.parse() because they
			// are bypassed for a known device.
			// Devices may send two SSDP responses, one with an IPv4 location
			// and one with an IPv6 location.
			// Do these check BEFORE we call dev.setSSDPPacket() so we don't
			// overwrite the SSDPPacket in DeviceData.
			// TODO handle multiple locations in DeviceData.
			String host = locationUrl.getHost();
			if (host == null) {
				Debug.warning("Ignoring device with bad URL at " + location);
				return;
			}
			if (host.startsWith("127.")) {
				Debug.warning("Ignoring localhost device at " + location);
				return;
			}
			if (host.startsWith("[") && host.endsWith("]")) {
				if (!ALLOW_IPV6_LOCATION) {
					Debug.warning("Ignoring IPv6 device at " + location);
					return;
				}
				// fixup for valid checks below
				host = host.substring(1, host.length() - 1);
			}
			if (!"http".equals(locationUrl.getProtocol())) {
				Debug.warning("Ignoring non-http device at " + location);
				return;
			}
			if (!Addresses.isIPv4Address(host) &&
			    (!ALLOW_IPV6_LOCATION || !Addresses.isIPv6Address(host))) {
				Debug.warning("Ignoring non-IPv4 address at " + location);
				return;
			}
			byte[] ip = Addresses.getIP(host);
			if (ip == null) {
				Debug.warning("Ignoring bad IP at " + location);
				return;
			}
			if (TransportUtil.isPubliclyRoutable(ip, ALLOW_IPV6_LOCATION)) {
				Debug.warning("Ignoring public address at " + location);
				return;
			}
			String udn = USN.getUDN(usn);
			Device dev = getDevice(udn);
			if (dev != null) {
				Debug.message("Additional SSDP for " + udn + " at " + location);
				dev.setSSDPPacket(ssdpPacket);
				return;
			}

			Parser parser = UPnP.getXMLParser();
			Node rootNode = parser.parse(locationUrl);
			Device rootDev = getDevice(rootNode);
			if (rootDev == null)
				return;
			rootDev.setSSDPPacket(ssdpPacket);
			Debug.warning("Add root device at " + location, new Exception("received on " + ssdpPacket.getLocalAddress()));
			addDevice(rootNode);

			// Thanks for Oliver Newell (2004/10/16)
			// After node is added, invoke the AddDeviceListener to notify high-level 
			// control point application that a new device has been added. (The 
			// control point application must implement the DeviceChangeListener interface
			// to receive the notifications)
			performAddDeviceListener( rootDev );
		}
		catch (MalformedURLException me) {
			Debug.warning("Bad location: " + location, me);
		}
		catch (ParserException pe) {
			Debug.warning("Error parsing data at location: " + location, pe);
		}
	}

	private Device getDevice(Node rootNode)
	{
		if (rootNode == null)
				return null;
		Node devNode = rootNode.getNode(Device.ELEM_NAME);
		if (devNode == null)
				return null;
		return new Device(rootNode, devNode);
	}

	public DeviceList getDeviceList()
	{
		DeviceList devList = new DeviceList();
		int nRoots = devNodeList.size();
		for (int n=0; n (09/08/03)
		if (httpReq.isNotifyRequest() == true) {
			NotifyRequest notifyReq = new NotifyRequest(httpReq);
			String uuid = notifyReq.getSID();
			long seq = notifyReq.getSEQ();
			PropertyList props = notifyReq.getPropertyList();
			int propCnt = props.size();
			for (int n = 0; n < propCnt; n++) {
				Property prop = props.getProperty(n);
				String varName = prop.getName();
				String varValue = prop.getValue();
				performEventListener(uuid, seq, varName, varValue);
			}
			httpReq.returnOK();
			return;
 		}
		
		httpReq.returnBadRequest();
	}

	////////////////////////////////////////////////
	//	Event Listener 
	////////////////////////////////////////////////

	private ListenerList eventListenerList = new ListenerList();
	 	
	public void addEventListener(EventListener listener)
	{
		eventListenerList.add(listener);
	}		

	public void removeEventListener(EventListener listener)
	{
		eventListenerList.remove(listener);
	}		

	public void performEventListener(String uuid, long seq, String name, String value)
	{
		int listenerSize = eventListenerList.size();
		for (int n=0; n




© 2015 - 2024 Weber Informatics LLC | Privacy Policy