/**
* UPnP PortMapper - A tool for managing port forwardings via UPnP
* Copyright (C) 2015 Christoph Pirkl <christoph at users.sourceforge.net>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.chris.portmapper;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.chris.portmapper.model.PortMapping;
import org.chris.portmapper.model.Protocol;
import org.chris.portmapper.router.AbstractRouterFactory;
import org.chris.portmapper.router.IRouter;
import org.chris.portmapper.router.RouterException;
import org.chris.portmapper.router.cling.ClingRouterFactory;
import org.jdesktop.application.Application;
import org.jdesktop.application.utils.AppHelper;
import org.jdesktop.application.utils.PlatformType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PortMapperCli {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final CommandLineArguments cmdLineArgs;
private String routerFactoryClassName = ClingRouterFactory.class.getName();
private Integer routerIndex = null;
public PortMapperCli() {
cmdLineArgs = new CommandLineArguments();
}
public void start(final String[] args) {
if (!cmdLineArgs.parse(args)) {
System.exit(1);
}
if (isStartGuiRequired()) {
startGui(args);
return;
}
if (cmdLineArgs.getUpnpLib() != null) {
this.routerFactoryClassName = cmdLineArgs.getUpnpLib();
logger.info("Using router factory class '" + this.routerFactoryClassName + "'");
}
if (cmdLineArgs.getRouterIndex() != null) {
this.routerIndex = cmdLineArgs.getRouterIndex();
logger.info("Using router index " + this.routerIndex);
}
if (cmdLineArgs.isPrintHelp()) {
printHelp();
return;
}
try {
final IRouter router = connect();
if (router == null) {
logger.error("No router found: exit");
System.exit(1);
return;
}
if (cmdLineArgs.isAddPortMapping()) {
addPortForwarding(router);
} else if (cmdLineArgs.isPrintInfo()) {
printStatus(router);
} else if (cmdLineArgs.isDeletePortMapping()) {
deletePortForwardings(router);
} else if (cmdLineArgs.isListPortMappings()) {
printPortForwardings(router);
} else {
router.disconnect();
System.err.println("Incorrect usage");
printHelp();
System.exit(1);
return;
}
router.disconnect();
} catch (final RouterException e) {
logger.error("An error occured", e);
System.exit(1);
return;
}
System.exit(0);
}
private void startGui(final String[] args) {
if (AppHelper.getPlatform() == PlatformType.OS_X) {
MacSetup.setupMac();
}
Application.launch(PortMapperApp.class, args);
}
private void printPortForwardings(final IRouter router) throws RouterException {
final Collection<PortMapping> mappings = router.getPortMappings();
if (mappings.size() == 0) {
logger.info("No port mappings found");
return;
}
final StringBuilder b = new StringBuilder();
for (final Iterator<PortMapping> iterator = mappings.iterator(); iterator.hasNext();) {
final PortMapping mapping = iterator.next();
b.append(mapping.getCompleteDescription());
if (iterator.hasNext()) {
b.append("\n");
}
}
logger.info("Found " + mappings.size() + " port forwardings:\n" + b.toString());
}
private void deletePortForwardings(final IRouter router) throws RouterException {
final String remoteHost = null;
final int port = cmdLineArgs.getExternalPort();
final Protocol protocol = cmdLineArgs.getProtocol();
logger.info("Deleting mapping for protocol " + protocol + " and external port " + port);
router.removePortMapping(protocol, remoteHost, port);
printPortForwardings(router);
}
private void printStatus(final IRouter router) throws RouterException {
router.logRouterInfo();
}
private void addPortForwarding(final IRouter router) throws RouterException {
final String remoteHost = null;
final String internalClient = cmdLineArgs.getInternalIp() != null ? cmdLineArgs.getInternalIp()
: router.getLocalHostAddress();
final int internalPort = cmdLineArgs.getInternalPort();
final int externalPort = cmdLineArgs.getExternalPort();
final Protocol protocol = cmdLineArgs.getProtocol();
final String description = cmdLineArgs.getDescription() != null ? cmdLineArgs.getDescription()
: "PortMapper " + protocol + "/" + internalClient + ":" + internalPort;
final PortMapping mapping = new PortMapping(protocol, remoteHost, externalPort, internalClient, internalPort,
description);
logger.info("Adding mapping " + mapping);
router.addPortMapping(mapping);
printPortForwardings(router);
}
private void printHelp() {
cmdLineArgs.printHelp();
}
private boolean isStartGuiRequired() {
if (cmdLineArgs.isStartGui()) {
return true;
}
return !(cmdLineArgs.isPrintHelp() || cmdLineArgs.isAddPortMapping() || cmdLineArgs.isPrintInfo()
|| cmdLineArgs.isListPortMappings() || cmdLineArgs.isDeletePortMapping());
}
@SuppressWarnings("unchecked")
private AbstractRouterFactory createRouterFactory() throws RouterException {
Class<AbstractRouterFactory> routerFactoryClass;
logger.info("Creating router factory for class {}", routerFactoryClassName);
try {
routerFactoryClass = (Class<AbstractRouterFactory>) Class.forName(routerFactoryClassName);
} catch (final ClassNotFoundException e) {
throw new RouterException("Did not find router factory class for name " + routerFactoryClassName, e);
}
logger.debug("Creating a new instance of the router factory class {}", routerFactoryClass);
try {
final Constructor<AbstractRouterFactory> constructor = routerFactoryClass
.getConstructor(PortMapperApp.class);
return constructor.newInstance(new PortMapperApp());
} catch (final Exception e) {
throw new RouterException("Error creating a router factory using class " + routerFactoryClass.getName(), e);
}
}
private IRouter connect() throws RouterException {
AbstractRouterFactory routerFactory;
try {
routerFactory = createRouterFactory();
} catch (final RouterException e) {
logger.error("Could not create router factory", e);
return null;
}
logger.info("Searching for routers...");
final List<IRouter> foundRouters = routerFactory.findRouters();
return selectRouter(foundRouters);
}
/**
* @param foundRouters
* @return
*/
private IRouter selectRouter(final List<IRouter> foundRouters) {
// One router found: use it.
if (foundRouters.size() == 1) {
final IRouter router = foundRouters.iterator().next();
logger.info("Connected to router " + router.getName());
return router;
} else if (foundRouters.size() == 0) {
logger.error("Found no router");
return null;
} else if (foundRouters.size() > 1 && routerIndex == null) {
// let user choose which router to use.
logger.error("Found more than one router. Use option -i <index>");
int index = 0;
for (final IRouter iRouter : foundRouters) {
logger.error("- index " + index + ": " + iRouter.getName());
index++;
}
return null;
} else if (routerIndex >= 0 && routerIndex < foundRouters.size()) {
final IRouter router = foundRouters.get(routerIndex);
logger.info("Found more than one router, using " + router.getName());
return router;
} else {
logger.error("Index must be between 0 and " + (foundRouters.size() - 1));
return null;
}
}
}