// Copyright 2017 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package google.registry.whois; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.model.CacheUtils.memoizeWithShortExpiration; import static google.registry.util.RegistrarUtils.normalizeRegistrarName; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; import google.registry.model.registrar.Registrar; import google.registry.util.FormattingLogger; import java.util.HashMap; import java.util.List; import java.util.Map; import org.joda.time.DateTime; /** Represents a WHOIS lookup for a registrar by its name. */ final class RegistrarLookupCommand implements WhoisCommand { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); /** * Cache of a map from a stripped-down (letters and digits only) name to the registrar. This map * includes only active, publicly visible registrars, because the others should be invisible to * WHOIS. */ private static final Supplier> REGISTRAR_BY_NORMALIZED_NAME_CACHE = memoizeWithShortExpiration(new Supplier>() { @Override public Map get() { Map map = new HashMap<>(); // Use the normalized registrar name as a key. Iterable registrars = Registrar.loadAllActiveAndPubliclyVisible(); for (Registrar registrar : registrars) { if (registrar.getRegistrarName() == null) { continue; } String normalized = normalizeRegistrarName(registrar.getRegistrarName()); if (map.put(normalized, registrar) != null) { logger.warning(normalized + " appeared as a normalized registrar name for more than one registrar"); } } // Use the normalized registrar name without its last word as a key, assuming there are // multiple words in the name. This allows searches without LLC or INC, etc. Only insert // if there isn't already a mapping for this string, so that if there's a registrar with a // two word name (Go Daddy) and no business-type suffix and another registrar with just // that first word as its name (Go), the latter will win. for (Registrar registrar : registrars) { if (registrar.getRegistrarName() == null) { continue; } List words = Splitter.on(CharMatcher.whitespace()).splitToList(registrar.getRegistrarName()); if (words.size() > 1) { String normalized = normalizeRegistrarName(Joiner.on("").join(words.subList(0, words.size() - 1))); if (!map.containsKey(normalized)) { map.put(normalized, registrar); } } } return ImmutableMap.copyOf(map); }}); @VisibleForTesting final String registrarName; RegistrarLookupCommand(String registrarName) { checkArgument(!isNullOrEmpty(registrarName), "registrarName"); this.registrarName = registrarName; } @Override public WhoisResponse executeQuery(DateTime now) throws WhoisException { Registrar registrar = REGISTRAR_BY_NORMALIZED_NAME_CACHE.get().get(normalizeRegistrarName(registrarName)); // If a registrar is in the cache, we know it must be active and publicly visible. if (registrar == null) { throw new WhoisException(now, SC_NOT_FOUND, "No registrar found."); } return new RegistrarWhoisResponse(registrar, now); } }