net.spfbl.whois.Owner.java Source code

Java tutorial

Introduction

Here is the source code for net.spfbl.whois.Owner.java

Source

/*
 * This file is part of SPFBL.
 * 
 * SPFBL 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.
 * 
 * SPFBL 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 SPFBL.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.spfbl.whois;

import net.spfbl.core.Server;
import net.spfbl.core.ProcessException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.commons.lang3.SerializationUtils;

/**
 * Representa o registro de dono de um resultado WHOIS.
 * 
 * A chave primria dos registros  o atributo ownerid.
 * 
 * @author Leandro Carlos Rodrigues <leandro@spfbl.net>
 */
public class Owner implements Serializable, Comparable<Owner> {

    private static final long serialVersionUID = 1L;

    private String owner; // Nome do dono.
    private final String ownerid; // Identificao do dono.
    private String responsible; // Responsvel pelo registro.
    private String country; // Pas onde o dono foi registrado.
    private String owner_c; // Cdigo do dono.
    private Date created; // Data de criao do registro.
    private Date changed; // Data da alterao do registro.
    private String provider; // Provedor de responsvel.

    /**
     * Lista dos dominios registrados.
     */
    private final ArrayList<String> domainList = new ArrayList<String>();

    private String server = null; // Servidor onde a informao do registro pode ser encontrada.
    private long lastRefresh = 0; // ltima vez que houve atualizao do registro em milisegundos.
    private boolean reduced = false; // Diz se a ltima consulta foi reduzida.
    private int queries = 1; // Contador de consultas.

    private static int REFRESH_TIME = 84; // Prazo mximo que o registro deve permanecer em cache em dias.

    /**
     * Formatao padro dos campos de data do WHOIS.
     */
    private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd");

    /**
     * Atualiza o tempo de expirao do registro de domnio.
     * @param time tempo em dias que os registros de domno devem ser atualizados.
     */
    protected static void setRefreshTime(int time) {
        REFRESH_TIME = time;
    }

    /**
     * Verifica se o registro atual expirou.
     * @return verdadeiro se o registro atual expirou.
     */
    public boolean isRegistryExpired() {
        long expiredTime = (System.currentTimeMillis() - lastRefresh) / Server.DAY_TIME;
        return expiredTime > REFRESH_TIME;
    }

    /**
     * Verifica se a expresso  um CPNJ ou CPF.
     * @param id a identificao a ser verificada.
     * @return verdadeiro se a expresso  um CPNJ ou CPF.
     */
    public static boolean isOwnerID(String id) {
        return Pattern.matches("^([0-9]{3}\\.[0-9]{3}\\.[0-9]{3}-[0-9]{2})"
                + "|([0-9]{2,3}\\.[0-9]{3}\\.[0-9]{3}/[0-9]{4}-[0-9]{2})$", id);
    }

    /**
     * Verifica se a expresso  um CPNJ.
     * @param id a identificao a ser verificada.
     * @return verdadeiro se a expresso  um CPNJ.
     */
    public static boolean isOwnerCNPJ(String id) {
        return Pattern.matches("^([0-9]{2,3}\\.[0-9]{3}\\.[0-9]{3}/[0-9]{4}-[0-9]{2})$", id);
    }

    /**
     * Verifica se a expresso  um CPF.
     * @param id a identificao a ser verificada.
     * @return verdadeiro se a expresso  um CPF.
     */
    public static boolean isOwnerCPF(String id) {
        return Pattern.matches("^([0-9]{3}\\.[0-9]{3}\\.[0-9]{3}-[0-9]{2})$", id);
    }

    /**
     * Intancia um novo registro de domnio.
     * @param result o resultado do WHOIS.
     * @throws QueryException se houver alguma falha da atualizao do registro.
     */
    private Owner(String id) throws ProcessException {
        this.ownerid = normalizeID(id);
        this.refresh();
    }

    public static String normalizeID(String id) throws ProcessException {
        if (id == null) {
            return null;
        } else if (Pattern.matches("^[0-9]{3}\\.[0-9]{3}\\.[0-9]{3}-[0-9]{2}$", id)) {
            return id;
        } else if (Pattern.matches("^[0-9]{3}\\.[0-9]{3}\\.[0-9]{3}/[0-9]{4}-[0-9]{2}$", id)) {
            return id;
        } else if (Pattern.matches("^[0-9]{2}\\.[0-9]{3}\\.[0-9]{3}/[0-9]{4}-[0-9]{2}$", id)) {
            return "0" + id;
        } else {
            throw new ProcessException("ERROR: INVALID ID");
        }
    }

    private boolean refresh() throws ProcessException {
        server = Server.WHOIS_BR; // Temporrio at final de transio.
        String result = Server.whoisID(ownerid, server);
        String ownerResult = refresh(result);
        return ownerid.equals(ownerResult);
    }

    /**
     * Atualiza os campos do registro com resultado do WHOIS.
     * @param result o resultado do WHOIS.
     * @return o ownerid real apresentado no resultado do WHOIS.
     * @throws QueryException se houver alguma falha da atualizao do registro.
     */
    private String refresh(String result) throws ProcessException {
        try {
            boolean reducedLocal = false;
            String owneridResult = null;
            BufferedReader reader = new BufferedReader(new StringReader(result));
            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    if (line.startsWith("owner:")) {
                        int index = line.indexOf(':') + 1;
                        owner = line.substring(index).trim();
                    } else if (line.startsWith("ownerid:")) {
                        int index = line.indexOf(':') + 1;
                        owneridResult = line.substring(index).trim();
                    } else if (line.startsWith("responsible:")) {
                        int index = line.indexOf(':') + 1;
                        responsible = line.substring(index).trim();
                    } else if (line.startsWith("country:")) {
                        int index = line.indexOf(':') + 1;
                        country = line.substring(index).trim();
                    } else if (line.startsWith("owner-c:")) {
                        int index = line.indexOf(':') + 1;
                        owner_c = line.substring(index).trim();
                    } else if (line.startsWith("domain:")) {
                        int index = line.indexOf(':') + 1;
                        String domain = line.substring(index).trim();
                        domainList.add(domain);
                    } else if (line.startsWith("created:")) {
                        int index = line.indexOf(':') + 1;
                        String valor = line.substring(index).trim();
                        if (valor.startsWith("before ")) {
                            index = line.indexOf(' ');
                            valor = valor.substring(index);
                        }
                        created = DATE_FORMATTER.parse(valor);
                    } else if (line.startsWith("changed:")) {
                        int index = line.indexOf(':') + 1;
                        changed = DATE_FORMATTER.parse(line.substring(index).trim());
                    } else if (line.startsWith("provider:")) {
                        int index = line.indexOf(':') + 1;
                        provider = line.substring(index).trim();
                    } else if (line.startsWith("nic-hdl-br:")) {
                        int index = line.indexOf(':') + 1;
                        String nic_hdl_br = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String person = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String e_mail;
                        if (reducedLocal) {
                            e_mail = null;
                        } else {
                            e_mail = line.substring(index).trim();
                            line = reader.readLine().trim();
                            index = line.indexOf(':') + 1;
                        }
                        String created2 = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String changed2 = line.substring(index).trim();
                        Handle handle = Handle.getHandle(nic_hdl_br);
                        handle.setPerson(person);
                        handle.setEmail(e_mail);
                        handle.setCreated(created2);
                        handle.setChanged(changed2);
                    } else if (line.startsWith("% No match for domain")) {
                        throw new ProcessException("ERROR: OWNER NOT FOUND");
                    } else if (line.startsWith("% Permission denied.")) {
                        throw new ProcessException("ERROR: WHOIS DENIED");
                    } else if (line.startsWith("% Permisso negada.")) {
                        throw new ProcessException("ERROR: WHOIS DENIED");
                    } else if (line.startsWith("% Maximum concurrent connections limit exceeded")) {
                        throw new ProcessException("ERROR: WHOIS CONCURRENT");
                    } else if (line.startsWith("% Query rate limit exceeded. Reduced information.")) {
                        // Informao reduzida devido ao estouro de limite de consultas.
                        Server.removeWhoisQueryHour();
                        reducedLocal = true;
                    } else if (line.startsWith("% Query rate limit exceeded")) {
                        // Restrio total devido ao estouro de limite de consultas.
                        Server.removeWhoisQueryDay();
                        throw new ProcessException("ERROR: WHOIS QUERY LIMIT");
                    } else if (line.length() > 0 && Character.isLetter(line.charAt(0))) {
                        Server.logError("Linha no reconhecida: " + line);
                    }
                }
            } finally {
                reader.close();
            }
            if (owneridResult == null) {
                throw new ProcessException("ERROR: OWNER NOT FOUND");
            } else {
                this.lastRefresh = System.currentTimeMillis();
                this.reduced = reducedLocal;
                this.queries = 1;
                // Atualiza flag de atualizao.
                OWNER_CHANGED = true;
                // Retorna o ownerid real indicado pelo WHOIS.
                return owneridResult;
            }
        } catch (ProcessException ex) {
            throw ex;
        } catch (Exception ex) {
            Server.logError(ex);
            throw new ProcessException("ERROR: PARSING", ex);
        }
    }

    public Handle getOwner() {
        return Handle.getHandle(owner_c);
    }

    /**
     * Retorna o valor de um campo do registro ou o valor de uma funo.
     * @param key o campo do registro cujo valor deve ser retornado.
     * @return o valor de um campo do registro ou o valor de uma funo.
     * @throws ProcessException se houver falha no processamento.
     */
    public String get(String key, boolean updated) throws ProcessException {
        if (key.equals("owner")) {
            return owner;
        } else if (key.equals("ownerid")) {
            return ownerid;
        } else if (reduced && updated) {
            // Ultima consulta com informao reduzida.
            // Demais campos esto comprometidos.
            throw new ProcessException("ERROR: WHOIS QUERY LIMIT");
        } else if (key.equals("responsible")) {
            return responsible;
        } else if (key.equals("country")) {
            return country;
        } else if (key.equals("owner-c")) {
            return owner_c;
        } else if (key.equals("created")) {
            if (created == null) {
                return null;
            } else {
                return DATE_FORMATTER.format(created);
            }
        } else if (key.equals("changed")) {
            if (changed == null) {
                return null;
            } else {
                return DATE_FORMATTER.format(changed);
            }
        } else if (key.equals("provider")) {
            return provider;
        } else if (key.equals("domain")) {
            return domainList.toString();
        } else if (key.startsWith("owner-c/")) {
            int index = key.indexOf('/') + 1;
            key = key.substring(index);
            Handle owner = getOwner();
            if (owner == null) {
                return null;
            } else {
                return owner.get(key);
            }
        } else if (key.startsWith("domain/")) {
            int index = key.indexOf('/') + 1;
            key = key.substring(index);
            TreeSet<String> resultSet = new TreeSet<String>();
            for (String domainName : domainList) {
                Domain domain = Domain.getDomain(domainName);
                if (domain == null) {
                    return null;
                } else {
                    String result = domain.get(key, updated);
                    resultSet.add(domainName + "=" + result);
                }
            }
            return resultSet.toString();
        } else {
            return null;
        }
    }

    public static synchronized HashMap<String, Owner> getMap() {
        HashMap<String, Owner> map = new HashMap<String, Owner>();
        map.putAll(MAP);
        return map;
    }

    /**
     * Armazenamento de cache em disco.
     */
    public static void store() {
        if (OWNER_CHANGED) {
            try {
                //                Server.logTrace("storing owner.map");
                long time = System.currentTimeMillis();
                HashMap<String, Owner> map = getMap();
                File file = new File("./data/owner.map");
                FileOutputStream outputStream = new FileOutputStream(file);
                try {
                    SerializationUtils.serialize(map, outputStream);
                    // Atualiza flag de atualizao.
                    OWNER_CHANGED = false;
                } finally {
                    outputStream.close();
                }
                Server.logStore(time, file);
            } catch (Exception ex) {
                Server.logError(ex);
            }
        }
    }

    private static synchronized Owner put(String key, Owner owner) {
        return MAP.put(key, owner);
    }

    /**
     * Carregamento de cache do disco.
     */
    public static void load() {
        long time = System.currentTimeMillis();
        File file = new File("./data/owner.map");
        if (file.exists()) {
            try {
                HashMap<String, Object> map;
                FileInputStream fileInputStream = new FileInputStream(file);
                try {
                    map = SerializationUtils.deserialize(fileInputStream);
                } finally {
                    fileInputStream.close();
                }
                for (String key : map.keySet()) {
                    Object value = map.get(key);
                    if (value instanceof Owner) {
                        Owner owner = (Owner) value;
                        put(key, owner);
                    }
                }
                Server.logLoad(time, file);
            } catch (Exception ex) {
                Server.logError(ex);
            }
        }
    }

    /**
     * Mapa de domnios com busca de hash O(1).
     */
    private static final HashMap<String, Owner> MAP = new HashMap<String, Owner>();

    //    /**
    //     * Adciiona o registro de domnio no cache.
    //     * @param owner o owner que deve ser adicionado.
    //     */
    //    private static synchronized void addOwner(Owner owner) {
    //        MAP.put(owner.getOwnerID(), owner);
    //        // Atualiza flag de atualizao.
    //        OWNER_CHANGED = true;
    //    }

    //    /**
    //     * Remove o registro de domnio do cache.
    //     * @param owner o dono que deve ser removido.
    //     */
    //    private static synchronized void removeOwner(Owner owner) {
    //        if (MAP.drop(owner.getOwnerID()) != null) {
    //            // Atualiza flag de atualizao.
    //            OWNER_CHANGED = true;
    //        }
    //    }

    /**
     * Remove registro de domnio do cache.
     * @param id a identificao do dono que deve ser removido.
     * @return o registro de dono removido, se existir.
     * @throws ProcessException se houver falha no processamento.
     */
    public static synchronized Owner removeOwner(String id) throws ProcessException {
        String key = normalizeID(id);
        Owner owner = MAP.remove(key);
        // Atualiza flag de atualizao.
        OWNER_CHANGED = true;
        return owner;
    }

    /**
     * Flag que indica se o cache foi modificado.
     */
    private static boolean OWNER_CHANGED = false;

    /**
     * Atualiza o registro de domnio de um determinado host.
     * @param id a identificao do dono que deve ser atualizado.
     * @throws ProcessException se houver falha no processamento.
     */
    public static synchronized void refreshOwner(String id) throws ProcessException {
        String key = normalizeID(id);
        // Busca eficiente O(1).
        if (MAP.containsKey(key)) {
            // Owner encontrado.
            Owner owner = MAP.get(key);
            // Atualizando campos do registro.
            if (!owner.refresh()) {
                // Owner real do resultado WHOIS no bate com o registro.
                // Apagando registro de dono do cache.
                if (MAP.remove(owner.getOwnerID()) != null) {
                    // Atualiza flag de atualizao.
                    OWNER_CHANGED = true;
                }
                // Segue para nova consulta.
            }
        }
        // No encontrou o dono em cache.
        // Selecionando servidor da pesquisa WHOIS.
        String server = Server.WHOIS_BR;
        // Realizando a consulta no WHOIS.
        Owner owner = new Owner(key);
        owner.server = server; // Temporrio at final de transio.
        // Adicinando registro em cache.
        MAP.put(owner.getOwnerID(), owner);
        OWNER_CHANGED = true;
    }

    /**
     * Retorna o registro de domnio de um determinado host.
     * @param id a identificao do dono que deve ser retornado.
     * @return o registro de domnio de um determinado host.
     * @throws ProcessException se houver falha no processamento.
     */
    public static synchronized Owner getOwner(String id) throws ProcessException {
        String key = normalizeID(id);
        // Busca eficiente O(1).
        if (MAP.containsKey(key)) {
            // Owner encontrado.
            Owner owner = MAP.get(key);
            owner.queries++;
            if (owner.isRegistryExpired()) {
                // Registro desatualizado.
                // Atualizando campos do registro.
                if (owner.refresh()) {
                    // Owner real do resultado WHOIS bate com o registro.
                    return owner;
                } else if (MAP.remove(owner.getOwnerID()) != null) {
                    // Owner real do resultado WHOIS no bate com o registro.
                    OWNER_CHANGED = true;
                    // Segue para nova consulta.
                }
            } else {
                // Registro atualizado.
                return owner;
            }
        }
        // No encontrou o dominio em cache.
        // Selecionando servidor da pesquisa WHOIS.
        String server = Server.WHOIS_BR;
        // Realizando a consulta no WHOIS.
        Owner owner = new Owner(key);
        owner.server = server; // Temporrio at final de transio.
        // Adicinando registro em cache.
        MAP.put(owner.getOwnerID(), owner);
        OWNER_CHANGED = true;
        return owner;
    }

    /**
     * Retorna a identificao do dono.
     * @return a identificao do dono.
     */
    public String getOwnerID() {
        return ownerid;
    }

    @Override
    public int hashCode() {
        return ownerid.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Owner) {
            return equals((Owner) other);
        } else {
            return false;
        }
    }

    /**
     * Verifica se o registro atual  o mesmo de outro.
     * @param other o outro registro a ser comparado.
     * @return verdadeiro se o registro passado  igual ao atual.
     */
    public boolean equals(Owner other) {
        if (other == null) {
            return false;
        } else {
            return this.ownerid.equals(other.ownerid);
        }
    }

    @Override
    public int compareTo(Owner other) {
        return this.owner.compareTo(other.owner);
    }

    @Override
    public String toString() {
        return owner;
    }
}