/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.network.upnp;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class IGD {
protected static Logger log = UPnP.log;
String descriptionUrl;
String baseUrl;
String presentationUrl;
IGDdataService cif;
IGDdataService first;
IGDdataService second;
IGDdataService ipV6FC;
protected static class IGDdataService {
String serviceType;
String controlUrl;
String eventSubUrl;
String scpdUrl;
@Override
public String toString() {
return String.format("serviceType=%s[controlUrl=%s, eventSubUrl=%s, scpdUrl=%s]", serviceType, controlUrl, eventSubUrl, scpdUrl);
}
}
public IGD() {
}
public void discover(String descriptionUrl) {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setIgnoringElementContentWhitespace(true);
documentBuilderFactory.setIgnoringComments(true);
documentBuilderFactory.setCoalescing(true);
try {
URL url = new URL(descriptionUrl);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document description = documentBuilder.parse(url.openStream());
parseIGDdata(description);
this.descriptionUrl = descriptionUrl;
} catch (ParserConfigurationException e) {
log.error("Discovery", e);
} catch (SAXException e) {
log.error("Discovery", e);
} catch (MalformedURLException e) {
log.error("Discovery", e);
} catch (IOException e) {
log.error("Discovery", e);
}
}
public boolean isValid() {
return first != null && first.serviceType != null;
}
public boolean isConnected(UPnP upnp) {
return "Connected".equals(upnp.getStatusInfo(buildUrl(first.controlUrl), first.serviceType));
}
public String getExternalIPAddress(UPnP upnp) {
return upnp.getExternalIPAddress(buildUrl(first.controlUrl), first.serviceType);
}
public void addPortMapping(UPnP upnp, String remoteHost, int externalPort, String protocol, int internalPort, String internalClient, String description, int leaseDuration) {
if (first != null) {
upnp.addPortMapping(buildUrl(first.controlUrl), first.serviceType, remoteHost, externalPort, protocol, internalPort, internalClient, description, leaseDuration);
}
}
public void deletePortMapping(UPnP upnp, String remoteHost, int externalPort, String protocol) {
if (first != null) {
upnp.deletePortMapping(buildUrl(first.controlUrl), first.serviceType, remoteHost, externalPort, protocol);
}
}
private void parseIGDdata(Document description) {
baseUrl = null;
presentationUrl = null;
cif = null;
first = null;
second = null;
ipV6FC = null;
parseElement(description.getDocumentElement());
if (log.isDebugEnabled()) {
log.debug(String.format("IGD data: %s", toString()));
}
}
private void parseElement(Element element) {
if ("service".equals(element.getNodeName())) {
parseService(element);
} else if ("URLBase".equals(element.getNodeName())) {
baseUrl = getContent(element);
} else if ("presentationURL".equals(element.getNodeName())) {
presentationUrl = getContent(element);
} else {
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
parseElement((Element) node);
}
}
}
}
private String getContent(Node node) {
if (node.hasChildNodes()) {
return getContent(node.getChildNodes());
}
return node.getNodeValue();
}
private String getContent(NodeList nodeList) {
if (nodeList == null || nodeList.getLength() <= 0) {
return null;
}
StringBuilder content = new StringBuilder();
int n = nodeList.getLength();
for (int i = 0; i < n; i++) {
Node node = nodeList.item(i);
content.append(getContent(node));
}
return content.toString();
}
private String getNodeValue(Element element, String nodeName) {
return getContent(element.getElementsByTagName(nodeName));
}
private void parseService(Element element) {
String serviceType = getNodeValue(element, "serviceType");
IGDdataService dataService = null;
if ("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1".equals(serviceType)) {
cif = new IGDdataService();
dataService = cif;
} else if ("urn:schemas-upnp-org:service:WANIPv6FirewallControl:1".equals(serviceType)) {
ipV6FC = new IGDdataService();
dataService = ipV6FC;
} else if ("urn:schemas-upnp-org:service:WANIPConnection:1".equals(serviceType) ||
"urn:schemas-upnp-org:service:WANPPPConnection:1".equals(serviceType)) {
if (first == null) {
first = new IGDdataService();
dataService = first;
} else if (second == null) {
second = new IGDdataService();
dataService = second;
}
}
if (dataService != null) {
dataService.serviceType = serviceType;
dataService.controlUrl = getNodeValue(element, "controlURL");
dataService.eventSubUrl = getNodeValue(element, "eventSubURL");
dataService.scpdUrl = getNodeValue(element, "SCPDURL");
}
}
protected String buildUrl(String url) {
if (url.matches("^https?://.*")) {
return url;
}
StringBuilder completeUrl = new StringBuilder();
if (baseUrl != null && baseUrl.length() > 0) {
completeUrl.append(baseUrl);
} else {
completeUrl.append(descriptionUrl);
}
int firstColon = completeUrl.indexOf(":");
if (firstColon >= 0) {
int firstSep = completeUrl.indexOf("/", firstColon + 3);
if (firstSep >= 0) {
completeUrl.setLength(firstSep);
}
}
if (!url.startsWith("/")) {
completeUrl.append("/");
}
completeUrl.append(url);
return completeUrl.toString();
}
@Override
public String toString() {
return String.format("urlBase=%s, presentationUrl=%s, CIF: %s, first: %s, second: %s, IPv6FC: %s", baseUrl, presentationUrl, cif, first, second, ipV6FC);
}
}