Permalink
Browse files

Added configuration option for forcing renderer per IP address.

Fixes #115.
  • Loading branch information...
1 parent d6bd8c1 commit d5b2961520bb003757233375b7f56ff5e86fd112 @Raptor399 Raptor399 committed Jul 13, 2013
View
@@ -62,7 +62,8 @@ Changelog:
- Show user GPL licence during Windows installation
- Windows installer now runs PS3 Media Server with normal user rights after installation is complete
- Windows installer localization.
- - New Mac OSX installer with bundled JRE (pms-setup-macosx.tar.gz)
+ - New Mac OSX installer with bundled JRE (pms-setup-macosx.tar.gz)
+ - Added configuration option for forcing renderer per IP address (#115)
1.82.0 - 2013-06-03
@@ -184,6 +184,24 @@ renderer_default =
# Default: false
renderer_force_default =
+# Set the string that contains a list of IP adresses and the renderer
+# profiles that should be forced to match for them. The comma separated
+# list contains pairs of a renderer name followed by "@" and an IP
+# address. The renderer name should match the value of the RendererName
+# property in a .conf file. The IP address can be specified as a range.
+# If there are multiple matches, the first that is found will be forced.
+#
+# Example: "Sony Bravia HX@192.168.1.34,Sony Bravia EX@192.168.0-1.*" will
+# use regular detection for a renderer connecting from "192.168.2.1" (as
+# there is no match), will force "Sony Bravia EX" for a renderer connecting
+# from "192.168.1.2" (match with the range) and will force "Sony Bravia HX"
+# for a renderer connecting from "192.168.1.34" (matches both entries, but
+# the HX was found first).
+#
+# Default: "", which means PMS will not force anything and use regular
+# detection.
+renderer_force_ip =
+
# ---< Plugins >--------------------------------------------------------------
# The directory where PMS can find the plugins.
@@ -14,6 +14,7 @@
# RendererName: Determines the name that is displayed in the PMS user
# interface when this renderer connects.
+# Do not use "@" or "," in the name.
RendererName = PlayStation 3
# RendererIcon: Determines the icon that is displayed in the PMS user
@@ -248,6 +248,23 @@ public String getNormalizedFilter() {
return b.toString();
}
+ /**
+ * Decides whether or not the IP address matches this filter. If the filter
+ * is empty, false is returned.
+ *
+ * @param addr The address to match.
+ * @return True if the address matches, false otherwise.
+ */
+ public boolean isMatch(InetAddress addr) {
+ for (Predicate predicate : matchers) {
+ if (predicate.match(addr)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public boolean allowed(InetAddress addr) {
boolean log = isFirstDecision(addr);
if (matchers.size() == 0) {
@@ -152,6 +152,7 @@
private static final String KEY_PROFILE_NAME = "name";
private static final String KEY_RENDERER_DEFAULT = "renderer_default";
private static final String KEY_RENDERER_FORCE_DEFAULT = "renderer_force_default";
+ private static final String KEY_RENDERER_FORCE_IP = "renderer_force_ip";
private static final String KEY_SERVER_HOSTNAME = "hostname";
private static final String KEY_SERVER_PORT = "port";
private static final String KEY_SHARES = "shares";
@@ -2151,6 +2152,42 @@ public void setRendererForceDefault(boolean value) {
configuration.setProperty(KEY_RENDERER_FORCE_DEFAULT, value);
}
+ /**
+ * Returns a string containing a list of IP addresses and the renderer
+ * profiles that should be forced to match them. Can be empty if no profile
+ * should be forced on any IP address.
+ *
+ * @return The string containing the list.
+ * @see #setRendererForceIp(String)
+ */
+ public String getRendererForceIp() {
+ return getString(KEY_RENDERER_FORCE_IP, "");
+ }
+
+ /**
+ * Set the string that contains a list of IP adresses and the renderer
+ * profiles that should be forced to match for them. The comma separated
+ * list contains pairs of a renderer name followed by "@" and an IP
+ * address. The renderer name should match the value of the RendererName
+ * property in a .conf file. The IP address can be specified as a range.
+ * If there are multiple matches, the first that is found will be forced.
+ * <p>
+ * Example: "Sony Bravia HX@192.168.1.34,Sony Bravia EX@192.168.0-1.*" will
+ * use regular detection for a renderer connecting from "192.168.2.1" (as
+ * there is no match), will force "Sony Bravia EX" for a renderer connecting
+ * from "192.168.1.2" (match with the range) and will force "Sony Bravia HX"
+ * for a renderer connecting from "192.168.1.34" (matches both entries, but
+ * the HX was found first).
+ * <p>
+ * Default: "", which means PMS will not force anything and use regular
+ * detection.
+ *
+ * @param value The string containing the list.
+ */
+ public void setRendererForceIp(String value) {
+ configuration.setProperty(KEY_RENDERER_FORCE_IP, value);
+ }
+
public String getVirtualFolders() {
return getString(KEY_VIRTUAL_FOLDERS, "");
}
@@ -246,8 +246,45 @@ public void associateIP(InetAddress sa) {
SpeedStats.getInstance().getSpeedInMBits(sa, getRendererName());
}
- public static RendererConfiguration getRendererConfigurationBySocketAddress(InetAddress sa) {
- return addressAssociation.get(sa);
+ /**
+ * Tries to find a matching renderer based on the configuration setting
+ * for forced IP address and renderer combinations. If there is no
+ * match, the address is looked up in the address association map which
+ * contains a mapping of previously encountered IP addresses and their
+ * renderers.
+ *
+ * @param inetAddress The IP address to look up.
+ * @return A renderer configuration or null if none can be found.
+ */
+ public static RendererConfiguration getRendererConfigurationBySocketAddress(InetAddress inetAddress) {
+ // First see if a renderer is forced for this address.
+ String forced = pmsConfiguration.getRendererForceIp();
+
+ if (forced != null && !"".equals(forced)) {
+ for (String tuple : forced.split(",")) {
+ if (tuple.indexOf("@") > -1) {
+ String name = tuple.split("@")[0];
+ String ip = tuple.split("@")[1];
+
+ // Sanity checks on the strings
+ if (!"".equals(name) && !"".equals(ip)) {
+ IpFilter filter = new IpFilter(ip);
+
+ if (filter.isMatch(inetAddress)) {
+ RendererConfiguration renderer = getRendererConfigurationByName(name);
+
+ if (renderer != null) {
+ logger.trace("Forcing renderer match to \"" + renderer.getRendererName() + "\" based on forced IP address configuration");
+ addressAssociation.put(inetAddress, renderer);
+ return renderer;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return addressAssociation.get(inetAddress);
}
/**
@@ -1124,4 +1161,11 @@ public boolean isRescaleByRenderer() {
public String getFFmpegVideoFilterOverride() {
return getString(OVERRIDE_VF, null);
}
+
+ /**
+ * Reset gathered information on IP address associations with renderers.
+ */
+ protected static void resetAddressAssociation() {
+ addressAssociation = new HashMap<InetAddress, RendererConfiguration>();
+ }
}
@@ -98,7 +98,7 @@ public void run() {
// header matches are attempted and if those fail as well we're stuck with the
// default renderer.
- // Attempt 1: try to recognize the renderer by its socket address from previous requests
+ // Attempt 1: try to recognize the renderer by its socket address
renderer = RendererConfiguration.getRendererConfigurationBySocketAddress(ia);
if (renderer != null) {
@@ -116,7 +116,7 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
// header matches are attempted and if those fail as well we're stuck with the
// default renderer.
- // Attempt 1: try to recognize the renderer by its socket address from previous requests
+ // Attempt 1: try to recognize the renderer by its socket address
renderer = RendererConfiguration.getRendererConfigurationBySocketAddress(ia);
if (renderer != null) {
@@ -19,18 +19,30 @@
package net.pms.configuration;
-import ch.qos.logback.classic.LoggerContext;
+import static net.pms.configuration.RendererConfiguration.getRendererConfigurationBySocketAddress;
+import static net.pms.configuration.RendererConfiguration.getRendererConfigurationByUA;
+import static net.pms.configuration.RendererConfiguration.getRendererConfigurationByUAAHH;
+import static net.pms.configuration.RendererConfiguration.loadRendererConfigurations;
+import static net.pms.configuration.RendererConfiguration.resetAddressAssociation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
import org.apache.commons.configuration.ConfigurationException;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.LoggerFactory;
-import java.util.*;
-import java.util.Map.Entry;
-
-import static net.pms.configuration.RendererConfiguration.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import ch.qos.logback.classic.LoggerContext;
/**
@@ -233,4 +245,125 @@ private void testHeader(String headerLine, String correctRendererName) {
}
}
}
+
+ /**
+ * Test {@link RendererConfiguration#getRendererConfigurationBySocketAddress(InetAddress)}
+ * @throws UnknownHostException
+ */
+ @Test
+ public void testGetBySocketAddress() throws UnknownHostException {
+ RendererConfiguration conf = null;
+ PmsConfiguration pmsConf = null;
+
+ try {
+ pmsConf = new PmsConfiguration(false);
+ } catch (ConfigurationException e) {
+ // This should be impossible since no configuration file will be loaded.
+ }
+
+ // Initialize the RendererConfiguration
+ loadRendererConfigurations(pmsConf);
+
+ // Nothing forced
+ pmsConf.setRendererForceIp("");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNull(conf);
+
+ // Garbled configuration: no renderer name
+ pmsConf.setRendererForceIp("@192.168.1.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNull(conf);
+
+ // Garbled configuration: no IP address
+ pmsConf.setRendererForceIp("PlayStation 3");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNull(conf);
+
+ // Garbled configuration: invalid IP address
+ pmsConf.setRendererForceIp("PlayStation 3@textip");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNull(conf);
+
+ // Garbled configuration: unknown renderer name
+ pmsConf.setRendererForceIp("No Match@192.168.1.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNull(conf);
+
+ // Garbled configuration: incorrect entries plus correct entry
+ pmsConf.setRendererForceIp("Sony Bravia EX,No Match@192.168.1.2,Playstation 3@192.168.1.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.2"));
+ assertNull(conf);
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+
+ // Set single forced IP address
+ pmsConf.setRendererForceIp("PlayStation 3@192.168.1.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.2"));
+ assertNull(conf);
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+
+ // Set forced IP address range
+ pmsConf.setRendererForceIp("PlayStation 3@192.168.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.23"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+
+ // Set forced IP address range
+ pmsConf.setRendererForceIp("PlayStation 3@192.168.0-1.*");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.2.1"));
+ assertNull(conf);
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.0.1"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.23"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+
+ // Set multiple forced IP addresses
+ pmsConf.setRendererForceIp("Sony Bravia EX@192.168.1.1,Sony Bravia HX@192.168.1.2");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.2.1"));
+ assertNull(conf);
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("Sony Bravia EX", conf.getRendererName());
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.2"));
+ assertNotNull(conf);
+ assertEquals("Sony Bravia HX", conf.getRendererName());
+
+ // Multiple addresses with range overlap
+ pmsConf.setRendererForceIp("Sony Bravia HX@192.168.1.2,Sony Bravia EX@192.168.0-1.*");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.2.1"));
+ assertNull(conf);
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("Sony Bravia EX", conf.getRendererName());
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.2"));
+ assertNotNull(conf);
+ assertEquals("Sony Bravia HX", conf.getRendererName());
+
+ // Renderer name matching is case insensitive
+ pmsConf.setRendererForceIp("playstation 3@192.168.1.1");
+ resetAddressAssociation();
+ conf = getRendererConfigurationBySocketAddress(InetAddress.getByName("192.168.1.1"));
+ assertNotNull(conf);
+ assertEquals("PlayStation 3", conf.getRendererName());
+
+ }
}

0 comments on commit d5b2961

Please sign in to comment.