001package com.pusher.rest.util;
002
003import java.security.InvalidKeyException;
004import java.security.NoSuchAlgorithmException;
005import java.util.Arrays;
006import java.util.HashSet;
007import java.util.List;
008import java.util.Map;
009import java.util.Set;
010import java.util.regex.Pattern;
011
012import javax.crypto.Mac;
013import javax.crypto.spec.SecretKeySpec;
014
015public final class Prerequisites {
016
017    private static final Pattern VALID_CHANNEL = Pattern.compile("\\A[-a-zA-Z0-9_=@,.;]+\\z");
018    private static final Pattern VALID_SOCKET_ID = Pattern.compile("\\A\\d+\\.\\d+\\z");
019
020    private static final Set<String> RESERVED_QUERY_KEYS = new HashSet<String>(
021            Arrays.asList(new String[] { "auth_key", "auth_timestamp", "auth_version", "auth_signature", "body_md5" }));
022
023    public static void nonNull(final String name, final Object ref) {
024        if (ref == null) throw new IllegalArgumentException("Parameter [" + name + "] must not be null");
025    }
026
027    public static void nonEmpty(final String name, final String ref) {
028        nonNull(name, ref);
029        if (ref.length() == 0) throw new IllegalArgumentException("Parameter [" + name + "] must not be empty");
030    }
031
032    public static void maxLength(final String name, final int max, final List<?> ref) {
033        if (ref.size() > max) throw new IllegalArgumentException("Parameter [" + name + "] must have size < " + max);
034    }
035
036    public static void noNullMembers(final String name, final List<?> ref) {
037        for (Object e : ref) {
038            if (e == null) throw new IllegalArgumentException("Parameter [" + name + "] must not contain null elements");
039        }
040    }
041
042    public static void noReservedKeys(final Map<String, String> params) {
043        for (String k : params.keySet()) {
044            if (RESERVED_QUERY_KEYS.contains(k.toLowerCase())) {
045                throw new IllegalArgumentException("Query parameter key [" + k + "] is reserved and should not be submitted. It will be generated by the signature generation.");
046            }
047        }
048    }
049
050    public static void isValidSha256Key(final String name, final String key) {
051        try {
052            Mac mac = Mac.getInstance("HmacSHA256");
053            mac.init(new SecretKeySpec(key.getBytes(), "SHA256"));
054            // If that goes OK, then we're good to go
055        }
056        catch (final NoSuchAlgorithmException e) {
057            // Out of luck.
058            throw new RuntimeException("The Pusher HTTP client requires HmacSHA256 support", e);
059        }
060        catch (final InvalidKeyException e) {
061            // Failed the test
062            throw new IllegalArgumentException("Parameter [" + name + "] must be a valid SHA256 key", e);
063        }
064    }
065
066    public static void areValidChannels(final List<String> channels) {
067        for (String channel : channels) {
068            isValidChannel(channel);
069        }
070    }
071
072    public static void isValidChannel(final String channel) {
073        matchesRegex("channel", VALID_CHANNEL, channel);
074    }
075
076    public static void isValidSocketId(final String socketId) {
077        if (socketId != null) {
078            matchesRegex("socket_id", VALID_SOCKET_ID, socketId);
079        }
080    }
081
082    private static void matchesRegex(final String name, final Pattern regex, final String toMatch) {
083        nonNull(name, toMatch);
084        if (!regex.matcher(toMatch).matches()) {
085            throw new IllegalArgumentException(name + " [" + toMatch + "] is not valid");
086        }
087    }
088}