Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
language: java

before_install:
- echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import
- echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust
- |
if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import
echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust
fi

install:
- mvn install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V
Expand Down
7 changes: 6 additions & 1 deletion conf.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ auth.password=
## Spotify authentication blob (BLOB)
auth.blob=
## Cache enabled
cache.enabled=false
cache.enabled=false
# Zeroconf
## Listen on all interfaces (overrides `zeroconf.interfaces`)
zeroconf.listenAll=true
## Listen on this interfaces (comma separated list of names)
zeroconf.interfaces=
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import org.jetbrains.annotations.Nullable;
import xyz.gianlu.librespot.core.AuthConfiguration;
import xyz.gianlu.librespot.core.Session;
import xyz.gianlu.librespot.core.ZeroconfAuthenticator;
import xyz.gianlu.librespot.player.CacheManager;
import xyz.gianlu.librespot.player.Player;

/**
* @author Gianlu
*/
public abstract class AbsConfiguration implements Player.PlayerConfiguration, CacheManager.CacheConfiguration, AuthConfiguration {
public abstract class AbsConfiguration implements Player.PlayerConfiguration, CacheManager.CacheConfiguration, AuthConfiguration, ZeroconfAuthenticator.Configuration {

@Nullable
public abstract String deviceName();
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/xyz/gianlu/librespot/DefaultConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,14 @@ public Session.DeviceType deviceType() {
public Strategy strategy() {
return Strategy.ZEROCONF;
}

@Override
public boolean zeroconfListenAll() {
return true;
}

@Override
public @NotNull String[] zeroconfInterfaces() {
return new String[0];
}
}
19 changes: 19 additions & 0 deletions core/src/main/java/xyz/gianlu/librespot/FileConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ private <E extends Enum<E>> E getEnum(@NotNull Class<E> clazz, @NotNull String k
}
}

@NotNull
private String[] getStringArray(@NotNull String key, @NotNull String[] fallback) {
String str = properties.getProperty(key, null);
if (str == null) return fallback;
else if ((str = str.trim()).isEmpty()) return new String[0];
else return Utils.split(str, ',');
}

@Override
public boolean cacheEnabled() {
return getBoolean("cache.enabled", defaults.cacheEnabled());
Expand Down Expand Up @@ -133,4 +141,15 @@ public float normalisationPregain() {
public Strategy strategy() {
return getEnum(Strategy.class, "auth.strategy", defaults.strategy());
}

@Override
public boolean zeroconfListenAll() {
return getBoolean("zeroconf.listenAll", defaults.zeroconfListenAll());
}

@NotNull
@Override
public String[] zeroconfInterfaces() {
return getStringArray("zeroconf.interfaces", defaults.zeroconfInterfaces());
}
}
6 changes: 5 additions & 1 deletion core/src/main/java/xyz/gianlu/librespot/core/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,12 @@ public static class Builder {
private final Inner inner;
private Authentication.LoginCredentials loginCredentials = null;
private AuthConfiguration authConf;
private ZeroconfAuthenticator.Configuration zeroconfConf;

public Builder(@NotNull DeviceType deviceType, @NotNull String deviceName, @NotNull AbsConfiguration configuration) {
this.inner = new Inner(deviceType, deviceName, configuration);
this.authConf = configuration;
this.zeroconfConf = configuration;
}

public Builder(@NotNull AbsConfiguration configuration) {
Expand All @@ -421,6 +424,7 @@ public Builder(@NotNull AbsConfiguration configuration) {

this.inner = new Inner(deviceType, deviceName, configuration);
this.authConf = configuration;
this.zeroconfConf = configuration;
}

public Builder facebook() throws IOException {
Expand All @@ -433,7 +437,7 @@ public Builder facebook() throws IOException {
}

public Builder zeroconf() throws IOException {
try (ZeroconfAuthenticator authenticator = new ZeroconfAuthenticator(inner)) {
try (ZeroconfAuthenticator authenticator = new ZeroconfAuthenticator(inner, zeroconfConf)) {
loginCredentials = authenticator.lockUntilCredentials();
return this;
} catch (InterruptedException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLDecoder;
import java.net.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.*;
Expand Down Expand Up @@ -65,22 +62,62 @@ public class ZeroconfAuthenticator implements Closeable {
private final Session.Inner session;
private final DiffieHellman keys;

ZeroconfAuthenticator(Session.Inner session) throws IOException {
ZeroconfAuthenticator(Session.Inner session, Configuration conf) throws IOException {
this.session = session;
this.keys = new DiffieHellman(session.random);
this.mDnsService = new MulticastDNSService();

int port = session.random.nextInt((MAX_PORT - MIN_PORT) + 1) + MIN_PORT;
new Thread(this.runner = new HttpRunner(port)).start();

ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), InetAddress.getAllByName("localhost"), "VERSION=1.0", "CPath=/");
InetAddress[] bound;
if (conf.zeroconfListenAll()) {
bound = getAllInterfacesAddresses();
} else {
String[] interfaces = conf.zeroconfInterfaces();
if (interfaces.length == 0) {
bound = new InetAddress[]{InetAddress.getLoopbackAddress()};
} else {
List<InetAddress> list = new ArrayList<>();
for (String str : interfaces) addAddressForInterfaceName(list, str);
bound = list.toArray(new InetAddress[0]);
}
}

LOGGER.debug("Registering service on " + Arrays.toString(bound));

ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), bound, "VERSION=1.0", "CPath=/");
spotifyConnectService = mDnsService.register(service);
if (spotifyConnectService == null)
throw new IOException("Failed registering SpotifyConnect service!");

LOGGER.info("SpotifyConnect service registered successfully!");
}

private static void addAddressForInterfaceName(List<InetAddress> list, @NotNull String name) throws SocketException {
NetworkInterface nif = NetworkInterface.getByName(name);
if (nif == null) {
LOGGER.warn(String.format("Interface %s doesn't exists.", name));
return;
}

addAddressOfInterface(list, nif);
}

private static void addAddressOfInterface(List<InetAddress> list, @NotNull NetworkInterface nif) {
LOGGER.trace(String.format("Adding addresses of %s (displayName: %s)", nif.getName(), nif.getDisplayName()));
Enumeration<InetAddress> ias = nif.getInetAddresses();
while (ias.hasMoreElements())
list.add(ias.nextElement());
}

@NotNull
private static InetAddress[] getAllInterfacesAddresses() throws SocketException {
List<InetAddress> list = new ArrayList<>();
Enumeration<NetworkInterface> is = NetworkInterface.getNetworkInterfaces();
while (is.hasMoreElements()) addAddressOfInterface(list, is.nextElement());
return list.toArray(new InetAddress[0]);
}

@Override
public void close() throws IOException {
mDnsService.unregister(spotifyConnectService);
Expand Down Expand Up @@ -194,6 +231,13 @@ Authentication.LoginCredentials lockUntilCredentials() throws InterruptedExcepti
}
}

public interface Configuration {
boolean zeroconfListenAll();

@NotNull
String[] zeroconfInterfaces();
}

private class HttpRunner implements Runnable, Closeable {
private final ServerSocket serverSocket;
private volatile boolean shouldStop = false;
Expand Down Expand Up @@ -297,4 +341,4 @@ public void close() throws IOException {
serverSocket.close();
}
}
}
}