/* This file is part of Libresonic. Libresonic 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. Libresonic 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 Libresonic. If not, see <http://www.gnu.org/licenses/>. Copyright 2016 (C) Libresonic Authors Based upon Subsonic, Copyright 2009 (C) Sindre Mehus */ package org.libresonic.player.service.upnp; import org.fourthline.cling.UpnpService; import org.fourthline.cling.model.action.ActionInvocation; import org.fourthline.cling.model.message.UpnpResponse; import org.fourthline.cling.model.meta.Device; import org.fourthline.cling.model.meta.Service; import org.fourthline.cling.support.igd.PortMappingListener; import org.fourthline.cling.support.igd.callback.PortMappingAdd; import org.fourthline.cling.support.igd.callback.PortMappingDelete; import org.fourthline.cling.support.model.PortMapping; import org.libresonic.player.service.UPnPService; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; /** * @author Sindre Mehus * @version $Id$ */ public class ClingRouter implements Router { private final Service connectionService; private final UpnpService upnpService; public static ClingRouter findRouter(UPnPService upnpService) { final Service connectionService = findConnectionService(upnpService.getUpnpService()); if (connectionService == null) { return null; } return new ClingRouter(connectionService, upnpService.getUpnpService()); } /** * Returns the UPnP service used for port mapping. */ private static Service findConnectionService(UpnpService upnpService) { class ConnectionServiceDiscoverer extends PortMappingListener { ConnectionServiceDiscoverer() { super(new PortMapping[0]); } @Override public Service discoverConnectionService(Device device) { return super.discoverConnectionService(device); } } ConnectionServiceDiscoverer discoverer = new ConnectionServiceDiscoverer(); Collection<Device> devices = upnpService.getRegistry().getDevices(); for (Device device : devices) { Service service = discoverer.discoverConnectionService(device); if (service != null) { return service; } } return null; } public ClingRouter(Service connectionService, UpnpService upnpService) { this.connectionService = connectionService; this.upnpService = upnpService; } public void addPortMapping(int externalPort, int internalPort, int leaseDuration) throws Exception { addPortMappingImpl(connectionService, internalPort); } public void deletePortMapping(int externalPort, int internalPort) throws Exception { deletePortMappingImpl(connectionService, internalPort); } private void addPortMappingImpl(Service connectionService, int port) throws Exception { final Semaphore gotReply = new Semaphore(0); final AtomicReference<String> error = new AtomicReference<String>(); upnpService.getControlPoint().execute( new PortMappingAdd(connectionService, createPortMapping(port)) { @Override public void success(ActionInvocation invocation) { gotReply.release(); } @Override public void failure(ActionInvocation invocation, UpnpResponse response, String defaultMsg) { error.set(String.valueOf(response) + ": " + defaultMsg); gotReply.release(); } } ); gotReply.acquire(); if (error.get() != null) { throw new Exception(error.get()); } } private void deletePortMappingImpl(Service connectionService, int port) throws Exception { final Semaphore gotReply = new Semaphore(0); upnpService.getControlPoint().execute( new PortMappingDelete(connectionService, createPortMapping(port)) { @Override public void success(ActionInvocation invocation) { gotReply.release(); } @Override public void failure(ActionInvocation invocation, UpnpResponse response, String defaultMsg) { gotReply.release(); } } ); gotReply.acquire(); } private PortMapping createPortMapping(int port) throws UnknownHostException { String localIp = InetAddress.getLocalHost().getHostAddress(); return new PortMapping(port, localIp, PortMapping.Protocol.TCP, "Libresonic"); } }