forked from I2P_Developers/i2p.i2p
1065 lines
28 KiB
Java
1065 lines
28 KiB
Java
/******************************************************************
|
|
*
|
|
* 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 <sassarol@cefriel.it>
|
|
* - 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 <newell@media-rush.com>
|
|
* - 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<nRoots; n++) {
|
|
// AIOOB was thrown from here, maybe would be better to
|
|
// copy the list before traversal?
|
|
Node rootNode;
|
|
try {
|
|
rootNode = devNodeList.getNode(n);
|
|
} catch (ArrayIndexOutOfBoundsException aioob) {
|
|
break;
|
|
}
|
|
Device dev = getDevice(rootNode);
|
|
if (dev == null)
|
|
continue;
|
|
devList.add(dev);
|
|
}
|
|
return devList;
|
|
}
|
|
|
|
public Device getDevice(String name)
|
|
{
|
|
int nRoots = devNodeList.size();
|
|
for (int n=0; n<nRoots; n++) {
|
|
// AIOOB was thrown from here, maybe would be better to
|
|
// copy the list before traversal?
|
|
Node rootNode;
|
|
try {
|
|
rootNode = devNodeList.getNode(n);
|
|
} catch (ArrayIndexOutOfBoundsException aioob) {
|
|
break;
|
|
}
|
|
Device dev = getDevice(rootNode);
|
|
if (dev == null)
|
|
continue;
|
|
if (dev.isDevice(name) == true)
|
|
return dev;
|
|
Device cdev = dev.getDevice(name);
|
|
if (cdev != null)
|
|
return cdev;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public boolean hasDevice(String name)
|
|
{
|
|
return (getDevice(name) != null) ? true : false;
|
|
}
|
|
|
|
private void removeDevice(Node rootNode)
|
|
{
|
|
// Thanks for Oliver Newell (2004/10/16)
|
|
// Invoke device removal listener prior to actual removal so Device node
|
|
// remains valid for the duration of the listener (application may want
|
|
// to access the node)
|
|
Device dev = getDevice(rootNode);
|
|
if( dev != null && dev.isRootDevice() )
|
|
performRemoveDeviceListener( dev );
|
|
|
|
devNodeList.remove(rootNode);
|
|
}
|
|
|
|
protected void removeDevice(Device dev)
|
|
{
|
|
if (dev == null)
|
|
return;
|
|
removeDevice(dev.getRootNode());
|
|
}
|
|
|
|
protected void removeDevice(String name)
|
|
{
|
|
Device dev = getDevice(name);
|
|
removeDevice(dev);
|
|
}
|
|
|
|
private void removeDevice(SSDPPacket packet)
|
|
{
|
|
if (packet.isByeBye() == false)
|
|
return;
|
|
String usn = packet.getUSN();
|
|
String udn = USN.getUDN(usn);
|
|
removeDevice(udn);
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// Expired Device
|
|
////////////////////////////////////////////////
|
|
|
|
private Disposer deviceDisposer;
|
|
private long expiredDeviceMonitoringInterval;
|
|
|
|
public void removeExpiredDevices()
|
|
{
|
|
DeviceList devList = getDeviceList();
|
|
int devCnt = devList.size();
|
|
Device dev[] = new Device[devCnt];
|
|
for (int n=0; n<devCnt; n++)
|
|
dev[n] = devList.getDevice(n);
|
|
for (int n=0; n<devCnt; n++) {
|
|
if (dev[n].isExpired() == true) {
|
|
Debug.message("Expired device = " + dev[n].getFriendlyName());
|
|
removeDevice(dev[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setExpiredDeviceMonitoringInterval(long interval)
|
|
{
|
|
expiredDeviceMonitoringInterval = interval;
|
|
}
|
|
|
|
public long getExpiredDeviceMonitoringInterval()
|
|
{
|
|
return expiredDeviceMonitoringInterval;
|
|
}
|
|
|
|
public void setDeviceDisposer(Disposer disposer)
|
|
{
|
|
deviceDisposer = disposer;
|
|
}
|
|
|
|
public Disposer getDeviceDisposer()
|
|
{
|
|
return deviceDisposer;
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// Notify
|
|
////////////////////////////////////////////////
|
|
|
|
private ListenerList deviceNotifyListenerList = new ListenerList();
|
|
|
|
public void addNotifyListener(NotifyListener listener)
|
|
{
|
|
deviceNotifyListenerList.add(listener);
|
|
}
|
|
|
|
public void removeNotifyListener(NotifyListener listener)
|
|
{
|
|
deviceNotifyListenerList.remove(listener);
|
|
}
|
|
|
|
public void performNotifyListener(SSDPPacket ssdpPacket)
|
|
{
|
|
int listenerSize = deviceNotifyListenerList.size();
|
|
for (int n=0; n<listenerSize; n++) {
|
|
NotifyListener listener = (NotifyListener)deviceNotifyListenerList.get(n);
|
|
try{
|
|
listener.deviceNotifyReceived(ssdpPacket);
|
|
}catch(Exception e){
|
|
Debug.warning("NotifyListener returned an error:", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// SearchResponse
|
|
////////////////////////////////////////////////
|
|
|
|
private ListenerList deviceSearchResponseListenerList = new ListenerList();
|
|
|
|
public void addSearchResponseListener(SearchResponseListener listener)
|
|
{
|
|
deviceSearchResponseListenerList.add(listener);
|
|
}
|
|
|
|
public void removeSearchResponseListener(SearchResponseListener listener)
|
|
{
|
|
deviceSearchResponseListenerList.remove(listener);
|
|
}
|
|
|
|
public void performSearchResponseListener(SSDPPacket ssdpPacket)
|
|
{
|
|
int listenerSize = deviceSearchResponseListenerList.size();
|
|
for (int n=0; n<listenerSize; n++) {
|
|
SearchResponseListener listener = (SearchResponseListener)deviceSearchResponseListenerList.get(n);
|
|
try{
|
|
listener.deviceSearchResponseReceived(ssdpPacket);
|
|
}catch(Exception e){
|
|
Debug.warning("SearchResponseListener returned an error:", e);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Device status changes (device added or removed)
|
|
// Applications that support the DeviceChangeListener interface are
|
|
// notified immediately when a device is added to, or removed from,
|
|
// the control point.
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
ListenerList deviceChangeListenerList = new ListenerList();
|
|
|
|
public void addDeviceChangeListener(DeviceChangeListener listener)
|
|
{
|
|
deviceChangeListenerList.add(listener);
|
|
}
|
|
|
|
public void removeDeviceChangeListener(DeviceChangeListener listener)
|
|
{
|
|
deviceChangeListenerList.remove(listener);
|
|
}
|
|
|
|
public void performAddDeviceListener( Device dev )
|
|
{
|
|
int listenerSize = deviceChangeListenerList.size();
|
|
for (int n=0; n<listenerSize; n++) {
|
|
DeviceChangeListener listener = (DeviceChangeListener)deviceChangeListenerList.get(n);
|
|
listener.deviceAdded( dev );
|
|
}
|
|
}
|
|
|
|
public void performRemoveDeviceListener( Device dev )
|
|
{
|
|
int listenerSize = deviceChangeListenerList.size();
|
|
for (int n=0; n<listenerSize; n++) {
|
|
DeviceChangeListener listener = (DeviceChangeListener)deviceChangeListenerList.get(n);
|
|
listener.deviceRemoved( dev );
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// SSDPPacket
|
|
////////////////////////////////////////////////
|
|
|
|
public void notifyReceived(SSDPPacket packet)
|
|
{
|
|
if (packet.isRootDevice() == true) {
|
|
if (packet.isAlive() == true){
|
|
addDevice(packet);
|
|
}else if (packet.isByeBye() == true){
|
|
removeDevice(packet);
|
|
}
|
|
}
|
|
performNotifyListener(packet);
|
|
}
|
|
|
|
public void searchResponseReceived(SSDPPacket packet)
|
|
{
|
|
if (packet.isRootDevice() == true)
|
|
addDevice(packet);
|
|
performSearchResponseListener(packet);
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// M-SEARCH
|
|
////////////////////////////////////////////////
|
|
|
|
private int searchMx = SSDP.DEFAULT_MSEARCH_MX;
|
|
|
|
public int getSearchMx()
|
|
{
|
|
return searchMx;
|
|
}
|
|
|
|
public void setSearchMx(int mx)
|
|
{
|
|
searchMx = mx;
|
|
}
|
|
|
|
public void search(String target, int mx)
|
|
{
|
|
SSDPSearchRequest msReq = new SSDPSearchRequest(target, mx);
|
|
SSDPSearchResponseSocketList ssdpSearchResponseSocketList = getSSDPSearchResponseSocketList();
|
|
ssdpSearchResponseSocketList.post(msReq);
|
|
}
|
|
|
|
public void search(String target)
|
|
{
|
|
search(target, SSDP.DEFAULT_MSEARCH_MX);
|
|
}
|
|
|
|
public void search()
|
|
{
|
|
search(ST.ROOT_DEVICE, SSDP.DEFAULT_MSEARCH_MX);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////
|
|
// EventSub HTTPServer
|
|
////////////////////////////////////////////////
|
|
|
|
private HTTPServerList httpServerList = new HTTPServerList();
|
|
|
|
/** I2P was private */
|
|
protected HTTPServerList getHTTPServerList()
|
|
{
|
|
return httpServerList;
|
|
}
|
|
|
|
public void httpRequestRecieved(HTTPRequest httpReq)
|
|
{
|
|
if (Debug.isOn() == true)
|
|
httpReq.print();
|
|
|
|
// Thanks for Giordano Sassaroli <sassarol@cefriel.it> (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<listenerSize; n++) {
|
|
EventListener listener = (EventListener)eventListenerList.get(n);
|
|
listener.eventNotifyReceived(uuid, seq, name, value);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// Subscription
|
|
////////////////////////////////////////////////
|
|
|
|
private String eventSubURI = DEFAULT_EVENTSUB_URI;
|
|
|
|
public String getEventSubURI()
|
|
{
|
|
return eventSubURI;
|
|
}
|
|
|
|
public void setEventSubURI(String url)
|
|
{
|
|
eventSubURI = url;
|
|
}
|
|
|
|
private String getEventSubCallbackURL(String host)
|
|
{
|
|
return HostInterface.getHostURL(host, getHTTPPort(), getEventSubURI());
|
|
}
|
|
|
|
public boolean subscribe(Service service, long timeout)
|
|
{
|
|
if (service.isSubscribed() == true) {
|
|
String sid = service.getSID();
|
|
return subscribe(service, sid, timeout);
|
|
}
|
|
|
|
Device rootDev = service.getRootDevice();
|
|
if (rootDev == null)
|
|
return false;
|
|
String ifAddress = rootDev.getInterfaceAddress();
|
|
SubscriptionRequest subReq = new SubscriptionRequest();
|
|
subReq.setSubscribeRequest(service, getEventSubCallbackURL(ifAddress), timeout);
|
|
SubscriptionResponse subRes = subReq.post();
|
|
if (subRes.isSuccessful() == true) {
|
|
service.setSID(subRes.getSID());
|
|
service.setTimeout(subRes.getTimeout());
|
|
return true;
|
|
|
|
}
|
|
service.clearSID();
|
|
return false;
|
|
}
|
|
|
|
public boolean subscribe(Service service)
|
|
{
|
|
return subscribe(service, Subscription.INFINITE_VALUE);
|
|
}
|
|
|
|
public boolean subscribe(Service service, String uuid, long timeout)
|
|
{
|
|
SubscriptionRequest subReq = new SubscriptionRequest();
|
|
subReq.setRenewRequest(service, uuid, timeout);
|
|
if (Debug.isOn() == true)
|
|
subReq.print();
|
|
SubscriptionResponse subRes = subReq.post();
|
|
if (Debug.isOn() == true)
|
|
subRes.print();
|
|
if (subRes.isSuccessful() == true) {
|
|
service.setSID(subRes.getSID());
|
|
service.setTimeout(subRes.getTimeout());
|
|
return true;
|
|
}
|
|
service.clearSID();
|
|
return false;
|
|
}
|
|
|
|
public boolean subscribe(Service service, String uuid)
|
|
{
|
|
return subscribe(service, uuid, Subscription.INFINITE_VALUE);
|
|
}
|
|
|
|
public boolean isSubscribed(Service service)
|
|
{
|
|
if (service == null)
|
|
return false;
|
|
return service.isSubscribed();
|
|
}
|
|
|
|
public boolean unsubscribe(Service service)
|
|
{
|
|
SubscriptionRequest subReq = new SubscriptionRequest();
|
|
subReq.setUnsubscribeRequest(service);
|
|
SubscriptionResponse subRes = subReq.post();
|
|
if (subRes.isSuccessful() == true) {
|
|
service.clearSID();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void unsubscribe(Device device)
|
|
{
|
|
ServiceList serviceList = device.getServiceList();
|
|
int serviceCnt = serviceList.size();
|
|
for (int n=0; n<serviceCnt; n++) {
|
|
Service service = serviceList.getService(n);
|
|
if (service.hasSID() == true)
|
|
unsubscribe(service);
|
|
}
|
|
|
|
DeviceList childDevList = device.getDeviceList();
|
|
int childDevCnt = childDevList.size();
|
|
for (int n=0; n<childDevCnt; n++) {
|
|
Device cdev = childDevList.getDevice(n);
|
|
unsubscribe(cdev);
|
|
}
|
|
}
|
|
|
|
public void unsubscribe()
|
|
{
|
|
DeviceList devList = getDeviceList();
|
|
int devCnt = devList.size();
|
|
for (int n=0; n<devCnt; n++) {
|
|
Device dev = devList.getDevice(n);
|
|
unsubscribe(dev);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// getSubscriberService
|
|
////////////////////////////////////////////////
|
|
|
|
public Service getSubscriberService(String uuid)
|
|
{
|
|
DeviceList devList = getDeviceList();
|
|
int devCnt = devList.size();
|
|
for (int n=0; n<devCnt; n++) {
|
|
Device dev = devList.getDevice(n);
|
|
Service service = dev.getSubscriberService(uuid);
|
|
if (service != null)
|
|
return service;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// getSubscriberService
|
|
////////////////////////////////////////////////
|
|
|
|
public void renewSubscriberService(Device dev, long timeout)
|
|
{
|
|
ServiceList serviceList = dev.getServiceList();
|
|
int serviceCnt = serviceList.size();
|
|
for (int n=0; n<serviceCnt; n++) {
|
|
Service service = serviceList.getService(n);
|
|
if (service.isSubscribed() == false)
|
|
continue;
|
|
String sid = service.getSID();
|
|
boolean isRenewed = subscribe(service, sid, timeout);
|
|
if (isRenewed == false)
|
|
subscribe(service, timeout);
|
|
}
|
|
|
|
DeviceList cdevList = dev.getDeviceList();
|
|
int cdevCnt = cdevList.size();
|
|
for (int n=0; n<cdevCnt; n++) {
|
|
Device cdev = cdevList.getDevice(n);
|
|
renewSubscriberService(cdev, timeout);
|
|
}
|
|
}
|
|
|
|
public void renewSubscriberService(long timeout)
|
|
{
|
|
DeviceList devList = getDeviceList();
|
|
int devCnt = devList.size();
|
|
for (int n=0; n<devCnt; n++) {
|
|
Device dev = devList.getDevice(n);
|
|
renewSubscriberService(dev, timeout);
|
|
}
|
|
}
|
|
|
|
public void renewSubscriberService()
|
|
{
|
|
renewSubscriberService(Subscription.INFINITE_VALUE);
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// Subscriber
|
|
////////////////////////////////////////////////
|
|
|
|
private RenewSubscriber renewSubscriber;
|
|
|
|
public void setRenewSubscriber(RenewSubscriber sub)
|
|
{
|
|
renewSubscriber = sub;
|
|
}
|
|
|
|
public RenewSubscriber getRenewSubscriber()
|
|
{
|
|
return renewSubscriber;
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// run
|
|
////////////////////////////////////////////////
|
|
|
|
public boolean start(String target, int mx)
|
|
{
|
|
stop();
|
|
|
|
////////////////////////////////////////
|
|
// HTTP Server
|
|
////////////////////////////////////////
|
|
|
|
int retryCnt = 0;
|
|
int bindPort = getHTTPPort();
|
|
HTTPServerList httpServerList = getHTTPServerList();
|
|
while (httpServerList.open(bindPort) == false) {
|
|
retryCnt++;
|
|
if (UPnP.SERVER_RETRY_COUNT < retryCnt) {
|
|
Debug.warning("Failed to open HTTP event listener port " + bindPort);
|
|
// I2P do we really need this, or can we just break ?
|
|
return false;
|
|
}
|
|
// I2P go down not up so we don't run into other I2P things
|
|
setHTTPPort(bindPort - 1);
|
|
bindPort = getHTTPPort();
|
|
}
|
|
httpServerList.addRequestListener(this);
|
|
httpServerList.start();
|
|
|
|
////////////////////////////////////////
|
|
// Notify Socket
|
|
////////////////////////////////////////
|
|
|
|
SSDPNotifySocketList ssdpNotifySocketList = getSSDPNotifySocketList();
|
|
if (ssdpNotifySocketList.open() == false) {
|
|
Debug.warning("Failed to open SSDP notify port 1900");
|
|
return false;
|
|
}
|
|
ssdpNotifySocketList.setControlPoint(this);
|
|
ssdpNotifySocketList.start();
|
|
|
|
////////////////////////////////////////
|
|
// SeachResponse Socket
|
|
////////////////////////////////////////
|
|
|
|
int ssdpPort = getSSDPPort();
|
|
retryCnt = 0;
|
|
SSDPSearchResponseSocketList ssdpSearchResponseSocketList = getSSDPSearchResponseSocketList();
|
|
while (ssdpSearchResponseSocketList.open(ssdpPort) == false) {
|
|
retryCnt++;
|
|
if (UPnP.SERVER_RETRY_COUNT < retryCnt) {
|
|
Debug.warning("Failed to open SSDP search response port " + ssdpPort);
|
|
return false;
|
|
}
|
|
// I2P go down not up so we don't run into other I2P things
|
|
setSSDPPort(ssdpPort - 1);
|
|
ssdpPort = getSSDPPort();
|
|
}
|
|
ssdpSearchResponseSocketList.setControlPoint(this);
|
|
ssdpSearchResponseSocketList.start();
|
|
|
|
////////////////////////////////////////
|
|
// search root devices
|
|
////////////////////////////////////////
|
|
|
|
search(target, mx);
|
|
|
|
////////////////////////////////////////
|
|
// Disposer
|
|
////////////////////////////////////////
|
|
|
|
Disposer disposer = new Disposer(this);
|
|
setDeviceDisposer(disposer);
|
|
disposer.start();
|
|
|
|
////////////////////////////////////////
|
|
// Subscriber
|
|
////////////////////////////////////////
|
|
|
|
if (isNMPRMode() == true) {
|
|
RenewSubscriber renewSub = new RenewSubscriber(this);
|
|
setRenewSubscriber(renewSub);
|
|
renewSub.start();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean start(String target)
|
|
{
|
|
return start(target, SSDP.DEFAULT_MSEARCH_MX);
|
|
}
|
|
|
|
public boolean start()
|
|
{
|
|
return start(ST.ROOT_DEVICE, SSDP.DEFAULT_MSEARCH_MX);
|
|
}
|
|
|
|
public boolean stop()
|
|
{
|
|
unsubscribe();
|
|
|
|
SSDPNotifySocketList ssdpNotifySocketList = getSSDPNotifySocketList();
|
|
ssdpNotifySocketList.stop();
|
|
ssdpNotifySocketList.close();
|
|
ssdpNotifySocketList.clear();
|
|
|
|
SSDPSearchResponseSocketList ssdpSearchResponseSocketList = getSSDPSearchResponseSocketList();
|
|
ssdpSearchResponseSocketList.stop();
|
|
ssdpSearchResponseSocketList.close();
|
|
ssdpSearchResponseSocketList.clear();
|
|
|
|
HTTPServerList httpServerList = getHTTPServerList();
|
|
httpServerList.stop();
|
|
httpServerList.close();
|
|
httpServerList.clear();
|
|
|
|
////////////////////////////////////////
|
|
// Disposer
|
|
////////////////////////////////////////
|
|
|
|
Disposer disposer = getDeviceDisposer();
|
|
if (disposer != null) {
|
|
disposer.stop();
|
|
setDeviceDisposer(null);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Subscriber
|
|
////////////////////////////////////////
|
|
|
|
RenewSubscriber renewSub = getRenewSubscriber();
|
|
if (renewSub != null) {
|
|
renewSub.stop();
|
|
setRenewSubscriber(null);
|
|
}
|
|
|
|
// I2P so we will re-notify on restart
|
|
DeviceList dl = getDeviceList();
|
|
for (int i = 0; i < dl.size(); i++) {
|
|
removeDevice(dl.getDevice(i));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// userData
|
|
////////////////////////////////////////////////
|
|
|
|
private Object userData = null;
|
|
|
|
public void setUserData(Object data)
|
|
{
|
|
userData = data;
|
|
}
|
|
|
|
public Object getUserData()
|
|
{
|
|
return userData;
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// print
|
|
////////////////////////////////////////////////
|
|
|
|
public void print()
|
|
{
|
|
DeviceList devList = getDeviceList();
|
|
int devCnt = devList.size();
|
|
Debug.message("Device Num = " + devCnt);
|
|
for (int n=0; n<devCnt; n++) {
|
|
Device dev = devList.getDevice(n);
|
|
Debug.message("[" + n + "] " + dev.getFriendlyName() + ", " + dev.getLeaseTime() + ", " + dev.getElapsedTime());
|
|
}
|
|
}
|
|
}
|