net.spfbl.core.Peer.java Source code

Java tutorial

Introduction

Here is the source code for net.spfbl.core.Peer.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.core;

import net.spfbl.data.Block;
import net.spfbl.data.Ignore;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import net.spfbl.data.Generic;
import net.spfbl.spf.SPF;
import net.spfbl.spf.SPF.Distribution;
import net.spfbl.spf.SPF.Status;
import net.spfbl.whois.Domain;
import net.spfbl.whois.Subnet;
import net.spfbl.whois.SubnetIPv6;
import org.apache.commons.lang3.SerializationUtils;

/**
 * Representa um peer do sistema P2P.
 * 
 * @author Leandro Carlos Rodrigues <leandro@spfbl.net>
 */
public final class Peer implements Serializable, Comparable<Peer> {

    private static final long serialVersionUID = 1L;

    private final String address; // Endereo de acesso ao peer.
    private short port; // Porta de acesso ao peer.
    private Send send = Send.REPUTATION; // Status de envio para este peer.
    private Receive receive = Receive.DROP; // Status de recebimento deste peer.
    private String email = null; // E-mail do responsvel.
    private int limit = 100;
    private NormalDistribution frequency = null;
    private long last = 0; // ltimo recebimento.
    private long create = System.currentTimeMillis(); // Data de criao.

    /**
     * Tabela de reputao do peer.
     */
    private final HashMap<String, Binomial> reputationMap = null;
    private TreeMap<String, Binomial> reputationMap2 = new TreeMap<String, Binomial>();
    private short reputationMax = 0;

    private Peer(Peer other) {
        this.address = other.address;
        this.port = other.port;
        this.send = other.send;
        this.receive = other.receive;
        this.email = other.email;
        this.limit = other.limit;
        if (other.frequency == null) {
            this.frequency = null;
        } else {
            this.frequency = other.frequency.replicate();
        }
        this.last = other.last;
        this.create = other.create;
        this.reputationMap2.putAll(other.getReputationMap());
        this.reputationMax = other.reputationMax;
    }

    //    /**
    //     * Retm os bloqueios que necessitam de confirmao.
    //     */
    //    private TreeSet<String> retainSet = new TreeSet<String>();

    public enum Send {
        NEVER, // Nunca enviar bloqueios e tabela de reputao para este peer.
        DUNNO, // Obsoleto.
        REPUTATION, // Sempre enviar somente a tabela de reputao.
        ALWAYS, // Sempre enviar os bloqueios e tabela de reputao.
        BLOCK, // Obsoleto.
        REPASS, // Envia e repassa todos os bloqueios recebidos.
        MIRROR // Trata o peer como espelho no envio.
    }

    public enum Receive {
        ACCEPT, // Aceita imediatamente os bloqueios sem repassar.
        REJECT, // Ignora todos os bloqueios e reputao recebidos.
        DROP, // Decarta todos os bloqueios recebidos e manda o firewall dropar.
        CONFIRM, // Obsoleto.
        REPUTATION, // Aceitar somente tabela de reputao.
        RETAIN, // Obsoleto.
        REPASS, // Aceita e repassa imediatamente os bloqueios.
        MIRROR // Trata o peer como espelho no recebimento.
    }

    private Peer(String address, int port) throws ProcessException {
        if (Domain.isHostname(address) || Subnet.isValidIP(address)) {
            this.address = Domain.extractHost(address, false);
            this.setPort(port);
        } else {
            throw new ProcessException("INVALID PEER");
        }
    }

    public boolean setPort(int port) throws ProcessException {
        if (port < 1024 || port >= 49152) {
            throw new ProcessException("INVALID PORT");
        } else if (this.port != port) {
            this.port = (short) port;
            CHANGED = true;
            return true;
        } else {
            return false;
        }
    }

    public boolean setPort(String port) throws ProcessException {
        try {
            int portInt = Integer.parseInt(port);
            return setPort(portInt);
        } catch (NumberFormatException ex) {
            throw new ProcessException("INVALID PORT", ex);
        }
    }

    public String getAddress() {
        return address;
    }

    public double getCorrelacao() {
        ArrayList<Float> xList = new ArrayList<Float>();
        ArrayList<Float> yList = new ArrayList<Float>();
        float xSum = 0;
        float ySum = 0;
        int n = 0;
        for (String token : getReputationKeySet()) {
            Binomial binomial = getReputation(token);
            if (binomial != null && binomial.getTotalSize() > 7) {
                Distribution distribution = SPF.getDistribution(token);
                if (distribution != null && distribution.getTotalSize() > 7) {
                    float x = binomial.getSpamProbability();
                    float y = distribution.getSpamProbability();
                    xSum += x;
                    ySum += y;
                    xList.add(x);
                    yList.add(y);
                    n++;
                }
            }
        }
        if (n > 32) {
            double xAvg = (double) xSum / n;
            double yAvg = (double) ySum / n;
            double coVar = 0.0d;
            xSum = 0;
            ySum = 0;
            for (int i = 0; i < n; i++) {
                float x = xList.get(i);
                float y = yList.get(i);
                coVar += (x - xAvg) * (y - yAvg);
                xSum += (x - xAvg) * (x - xAvg);
                ySum += (y - yAvg) * (y - yAvg);
            }
            return coVar / Math.sqrt(xSum * ySum);
        } else {
            return 0.0d;
        }
    }

    public InetAddress getInetAddress() throws UnknownHostException {
        return InetAddress.getByName(address);
    }

    public String getIP() throws UnknownHostException {
        InetAddress inetAddress = getInetAddress();
        String ip = inetAddress.getHostAddress();
        return Subnet.normalizeIP(ip);
    }

    public short getPort() {
        return port;
    }

    public Send getSendStatus() {
        return send;
    }

    public Receive getReceiveStatus() {
        return receive;
    }

    public String getEmail() {
        return email;
    }

    public User getUser() {
        return User.get(email);
    }

    public boolean isAlive() {
        return (System.currentTimeMillis() - last) / Server.DAY_TIME == 0;
    }

    public void setEmail(String email) throws ProcessException {
        if (email == null || email.length() == 0) {
            if (this.email != null) {
                this.email = null;
                CHANGED = true;
            }
        } else if (Domain.isEmail(email)) {
            if (!email.toLowerCase().equals(this.email)) {
                this.email = email.toLowerCase();
                CHANGED = true;
            }
        } else {
            throw new ProcessException("INVALID EMAIL");
        }
    }

    public boolean setSendStatus(Send status) throws ProcessException {
        if (status == null) {
            throw new ProcessException("INVALID SEND");
        } else if (status == Send.BLOCK) {
            throw new ProcessException("INVALID SEND");
        } else if (status == Send.DUNNO) {
            throw new ProcessException("INVALID SEND");
        } else if (status == this.send) {
            return false;
        } else {
            this.send = status;
            CHANGED = true;
            return true;
        }
    }

    public boolean setSendStatus(String status) throws ProcessException {
        try {
            return setSendStatus(Send.valueOf(status));
        } catch (IllegalArgumentException ex) {
            throw new ProcessException("INVALID SEND");
        }
    }

    public boolean setReceiveStatus(Receive status) throws ProcessException {
        if (status == null) {
            throw new ProcessException("INVALID RECEIVE");
        } else if (status == Receive.CONFIRM) {
            throw new ProcessException("INVALID RECEIVE");
        } else if (status == this.receive) {
            return false;
        } else {
            this.receive = status;
            CHANGED = true;
            return true;
        }
    }

    public boolean setReceiveStatus(String status) throws ProcessException {
        try {
            return setReceiveStatus(Receive.valueOf(status));
        } catch (IllegalArgumentException ex) {
            throw new ProcessException("INVALID RECEIVE");
        }
    }

    public synchronized TreeSet<String> getReputationKeySet() {
        TreeSet<String> keySet = new TreeSet<String>();
        if (reputationMap2 != null) {
            keySet.addAll(reputationMap2.keySet());
        }
        return keySet;
    }

    public TreeMap<String, Binomial> getReputationMap() {
        TreeMap<String, Binomial> returnSet = new TreeMap<String, Binomial>();
        for (String key : getReputationKeySet()) {
            Binomial binomial = getReputation(key);
            if (binomial != null) {
                returnSet.put(key, binomial);
            }
        }
        return returnSet;
    }

    //    public TreeSet<String> getRetationSet() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        returnSet.addAll(retainSet);
    //        return returnSet;
    //    }

    //    public static TreeSet<String> getAllRetationSet() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (Peer peer : Peer.getSet()) {
    //            for (String token : peer.getRetationSet()) {
    //                returnSet.add(peer.getAddress() + ':' + token);
    //            }
    //        }
    //        return returnSet;
    //    }

    //    public boolean dropExact(String token) {
    //        return retainSet.remove(token);
    //    }

    //    public String reject(String token) {
    //        if (dropExact(token)) {
    //            return "REJECTED";
    //        } else {
    //            return "NOT FOUND";
    //        }
    //    }

    //    public TreeSet<String> reject() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (String token : getRetationSet()) {
    //            String response = reject(token);
    //            if (!response.equals("NOT FOUND")) {
    //                returnSet.add(token + " => " + response);
    //            }
    //        }
    //        return returnSet;
    //    }

    public static TreeSet<String> getReputationKeyAllSet() {
        TreeSet<String> keySet = new TreeSet<String>();
        for (Peer peer : Peer.getSet()) {
            keySet.addAll(peer.getReputationKeySet());
        }
        return keySet;
    }

    //    public static TreeSet<String> rejectAll() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (Peer peer : Peer.getSet()) {
    //            for (String response : peer.reject()) {
    //                returnSet.add(peer.getAddress() + ':' + response);
    //            }
    //        }
    //        return returnSet;
    //    }

    //    public static TreeSet<String> rejectAll(String token) {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (Peer peer : Peer.getSet()) {
    //            String response = peer.reject(token);
    //            returnSet.add(peer.getAddress() + ':' + response);
    //        }
    //        return returnSet;
    //    }

    //    public String release(String token) {
    //        if (dropExact(token)) {
    //            try {
    //                if (Ignore.contains(token)) {
    //                    return "IGNORED";
    //                } else if (Block.addExact(token)) {
    //                    if (isReceiveRepass()) {
    //                        sendToOthers(token);
    //                        return "REPASSED";
    //                    } else {
    //                        sendToRepass(token);
    //                        return "ADDED";
    //                    }
    //                } else {
    //                    return "EXISTS";
    //                }
    //            } catch (ProcessException ex) {
    //                return ex.getErrorMessage();
    //            }
    //        } else {
    //            return "NOT FOUND";
    //        }
    //    }

    //    public TreeSet<String> release() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (String token : getRetationSet()) {
    //            String response = release(token);
    //            if (!response.equals("NOT FOUND")) {
    //                returnSet.add(token + " => " + response);
    //            }
    //        }
    //        return returnSet;
    //    }

    //    public static TreeSet<String> releaseAll() {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (Peer peer : Peer.getSet()) {
    //            for (String response : peer.release()) {
    //                returnSet.add(peer.getAddress() + ':' + response);
    //            }
    //        }
    //        return returnSet;
    //    }

    //    public static TreeSet<String> releaseAll(String token) {
    //        TreeSet<String> returnSet = new TreeSet<String>();
    //        for (Peer peer : Peer.getSet()) {
    //            String response = peer.release(token);
    //            returnSet.add(peer.getAddress() + ':' + response);
    //        }
    //        return returnSet;
    //    }

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

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

    public boolean drop() {
        return Peer.drop(address) != null;
    }

    public Peer clone(String hostname) throws ProcessException {
        Peer peerNew = create(hostname, port);
        peerNew.send = this.send;
        peerNew.receive = this.receive;
        peerNew.email = this.email;
        peerNew.limit = this.limit;
        peerNew.frequency = this.frequency.replicate();
        peerNew.last = this.last;
        peerNew.create = this.create;
        return peerNew;
    }

    public static Peer create(String hostname, String port) throws ProcessException {
        try {
            int portInt = Integer.parseInt(port);
            return create(hostname, portInt);
        } catch (NumberFormatException ex) {
            throw new ProcessException("INVALID PORT", ex);
        }
    }

    public synchronized static Peer create(String hostname, int port) throws ProcessException {
        hostname = Domain.extractHost(hostname, false);
        if (MAP.containsKey(hostname)) {
            return null;
        } else {
            Peer peer = new Peer(hostname, port);
            MAP.put(hostname, peer);
            CHANGED = true;
            return peer;
        }
    }

    public synchronized static TreeSet<Peer> getSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        peerSet.addAll(MAP.values());
        return peerSet;
    }

    public static TreeSet<Peer> getSendAllSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            switch (peer.getSendStatus()) {
            case REPUTATION:
            case BLOCK:
            case ALWAYS:
            case REPASS:
                peerSet.add(peer);
                break;
            }
        }
        return peerSet;
    }

    public static TreeSet<Peer> getReputationSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            switch (peer.getSendStatus()) {
            case BLOCK:
            case REPUTATION:
            case ALWAYS:
            case REPASS:
            case MIRROR:
                peerSet.add(peer);
                break;
            }
        }
        return peerSet;
    }

    public TreeSet<Peer> getSendSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            if (!peer.equals(this)) {
                switch (peer.getSendStatus()) {
                case BLOCK:
                case ALWAYS:
                case REPASS:
                    peerSet.add(peer);
                    break;
                }
            }
        }
        return peerSet;
    }

    public TreeSet<Peer> getRepassSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            if (!peer.equals(this)) {
                switch (peer.getSendStatus()) {
                case REPASS:
                    peerSet.add(peer);
                    break;
                }
            }
        }
        return peerSet;
    }

    public static TreeSet<Peer> getMirrorSet() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            if (peer.getSendStatus() == Send.MIRROR) {
                peerSet.add(peer);
            }
        }
        return peerSet;
    }

    public static TreeSet<Peer> dropAll() {
        TreeSet<Peer> peerSet = new TreeSet<Peer>();
        for (Peer peer : getSet()) {
            if (peer.drop()) {
                peerSet.add(peer);
            }
        }
        return peerSet;
    }

    public static Peer drop(String address) {
        Peer peer = MAP.remove(address);
        if (peer != null) {
            CHANGED = true;
        }
        return peer;
    }

    public static Peer get(String address) {
        if (address == null) {
            return null;
        } else {
            return MAP.get(address);
        }
    }

    public static Peer get(InetAddress inetAddress) {
        String ip = inetAddress.getHostAddress();
        ip = Subnet.normalizeIP(ip);
        for (Peer peer : getSet()) {
            //            try {
            //                if (ip.equals(peer.getIP())) {
            //                    return peer;
            //                }
            //            } catch (UnknownHostException ex) {
            //                Server.logDebug("peer '" + peer.getAddress() + "' has unknown host.");
            //            }
            if (SPF.matchHELO(ip, peer.getAddress())) {
                return peer;
            }
        }
        return null;
    }

    public static HashMap<String, Peer> getMap() {
        HashMap<String, Peer> map = new HashMap<String, Peer>();
        for (Peer peer : getSet()) {
            Peer clone = new Peer(peer);
            map.put(clone.getAddress(), clone);
        }
        return map;
    }

    //    public void sendAll() {
    //        if (Core.hasPeerConnection()) {
    //            TreeMap<String,Distribution> distributionSet = SPF.getDistributionMap();
    //            for (String token : distributionSet.keySet()) {
    //                Distribution distribution = distributionSet.get(token);
    //                if (distribution.isBlocked(token)) {
    //                    send(token);
    //                }
    //                try {
    //                    Thread.sleep(100);
    //                } catch (InterruptedException ex) {
    //                    // Interrompido.
    //                }
    //            }
    //        }
    //    }

    public void send(String token) {
        if (Core.hasPeerConnection()) {
            long time = System.currentTimeMillis();
            String origin = null;
            String address = getAddress();
            int port = getPort();
            String result = Core.sendCommandToPeer(token, address, port);
            Server.logPeerSend(time, origin, token, result);
        }
    }

    public void sendToOthers(String token) {
        long time = System.currentTimeMillis();
        if (Core.hasPeerConnection()) {
            String origin = null;
            String result = "SENT";
            try {
                for (Peer peer : getSendSet()) {
                    String address = peer.getAddress();
                    int port = peer.getPort();
                    Core.sendCommandToPeer(token, address, port);
                }
            } catch (Exception ex) {
                result = ex.getMessage();
            }
            Server.logPeerSend(time, origin, token, result);
        }
    }

    public static void sendToAll(String token) {
        long time = System.currentTimeMillis();
        if (Core.hasPeerConnection()) {
            String origin = null;
            String result = "SENT";
            try {
                for (Peer peer : getSendAllSet()) {
                    String address = peer.getAddress();
                    int port = peer.getPort();
                    Core.sendCommandToPeer(token, address, port);
                }
            } catch (Exception ex) {
                result = ex.getMessage();
            }
            Server.logPeerSend(time, origin, token, result);
        }
    }

    public static void sendBlockToAll(String token) {
        if (isValidBlock(token)) {
            long time = System.currentTimeMillis();
            if (Core.hasPeerConnection()) {
                String origin = null;
                String result = "SENT";
                String command = "BLOCK " + token;
                try {
                    for (Peer peer : getSendAllSet()) {
                        String address = peer.getAddress();
                        int port = peer.getPort();
                        Core.sendCommandToPeer(command, address, port);
                    }
                } catch (Exception ex) {
                    result = ex.getMessage();
                }
                Server.logPeerSend(time, origin, command, result);
            }
        }
    }

    /**
     * Mtodo de transio.
     * @param token
     * @param distribuiton 
     */
    public static void sendToAll(String token, Distribution distribuiton) {
        if (SPF.isValidReputation(token)) {
            long time = System.currentTimeMillis();
            if (Core.hasPeerConnection()) {
                int[] binomial;
                if (distribuiton == null) {
                    binomial = new int[2];
                } else {
                    binomial = distribuiton.getBinomial();
                }
                int ham = binomial[0];
                int spam = binomial[1];
                if (spam % 3 == 0) {
                    String origin = null;
                    String result = "SENT";
                    String command = "REPUTATION " + token + " " + ham + " " + spam;
                    try {
                        for (Peer peer : getReputationSet()) {
                            String address = peer.getAddress();
                            int port = peer.getPort();
                            Core.sendCommandToPeer(command, address, port);
                        }
                    } catch (Exception ex) {
                        result = ex.getMessage();
                    }
                    Server.logPeerSend(time, origin, command, result);
                }
            }
        }
    }

    public boolean sendHELO() {
        String connection = Core.getPeerConnection();
        if (connection == null) {
            return false;
        } else {
            String origin = null;
            String email = Core.getAdminEmail();
            String helo = "HELO " + connection + (email == null ? "" : " " + email);
            long time = System.currentTimeMillis();
            String address = getAddress();
            int port = getPort();
            String result = Core.sendCommandToPeer(helo, address, port);
            Server.log(time, Core.Level.DEBUG, "PEERP", origin, helo, result);
            return true;
        }
    }

    public static void sendHeloToAll() {
        long time = System.currentTimeMillis();
        String connection = Core.getPeerConnection();
        if (connection != null) {
            String origin = null;
            String result = "SENT";
            String email = Core.getAdminEmail();
            String helo = "HELO " + connection + (email == null ? "" : " " + email);
            try {
                for (Peer peer : getSendAllSet()) {
                    String address = peer.getAddress();
                    int port = peer.getPort();
                    Core.sendCommandToPeer(helo, address, port);
                }
            } catch (Exception ex) {
                result = ex.getMessage();
            }
            Server.log(time, Core.Level.DEBUG, "PEERP", origin, helo, result);
        }
    }

    public void sendToRepass(String token) {
        long time = System.currentTimeMillis();
        if (Core.hasPeerConnection()) {
            String origin = null;
            String result = "SENT";
            try {
                for (Peer peer : getRepassSet()) {
                    String address = peer.getAddress();
                    int port = peer.getPort();
                    Core.sendCommandToPeer(token, address, port);
                }
            } catch (Exception ex) {
                result = ex.getMessage();
            }
            Server.logPeerSend(time, origin, token, result);
        }
    }

    private static boolean isValidBlock(String token) {
        if (token == null || token.length() == 0) {
            return false;
        } else if (Subnet.isValidIP(token)) {
            return false;
        } else if (token.startsWith(".") && Domain.isHostname(token.substring(1))) {
            return true;
        } else if (token.contains("@") && Domain.isEmail(token)) {
            return true;
        } else if (token.startsWith("@") && Domain.containsDomain(token.substring(1))) {
            return true;
        } else {
            return false;
        }
    }

    //    private static boolean isValidReputation(String token) {
    //        if (token == null || token.length() == 0) {
    //            return false;
    //        } else if (Subnet.isValidIP(token)) {
    //            return true;
    //        } else if (token.startsWith(".") && Domain.isHostname(token.substring(1))) {
    //            return true;
    //        } else if (token.contains("@") && Domain.isEmail(token)) {
    //            return true;
    //        } else if (token.contains("#") && !token.contains("##") && Domain.isEmail(token.replace('#', '0'))) {
    //            return true;
    //        } else if (token.startsWith("@") && Domain.containsDomain(token.substring(1))) {
    //            return true;
    //        } else {
    //            return false;
    //        }
    //    }

    public static void sendToMirros(String command) {
        long time = System.currentTimeMillis();
        if (Core.hasPeerConnection()) {
            String origin = null;
            String result = "SENT";
            try {
                for (Peer mirror : getMirrorSet()) {
                    String address = mirror.getAddress();
                    int port = mirror.getPort();
                    Core.sendCommandToPeer(command, address, port);
                }
            } catch (Exception ex) {
                result = ex.getMessage();
            }
            Server.logPeerSend(time, origin, command, result);
        }
    }

    private boolean isReceiveDrop() {
        return receive == Receive.DROP;
    }

    private boolean isReceiveReject() {
        return receive == Receive.REJECT;
    }

    //    private boolean isReceiveRetain() {
    //        return receive == Receive.RETAIN;
    //    }

    private boolean isReceiveRepass() {
        return receive == Receive.REPASS;
    }

    //    private boolean addRetain(String token) {
    //        return retainSet.add(token);
    //    }

    //    @Deprecated
    //    public String processReceive(String token) {
    //        try {
    //            if (!isValidBlock(token)) {
    //                return "INVALID";
    //            } else if (Generic.contains(token)) {
    //                return "GENERIC";
    //            } else if (Domain.isReserved(token)) {
    //                return "RESERVED";
    //            } else if (Ignore.contains(token)) {
    //                return "IGNORED";
    //            } else if (isReceiveReject()) {
    //                return "REJECTED";
    //            } else if (isReceiveDrop()) {
    //                return "DROPPED";
    //            } else if (isReceiveRetain()) {
    //                if (addRetain(token)) {
    //                    return "RETAINED";
    //                } else {
    //                    return "DROPPED";
    //                }
    //            } else if (Block.addExact(token)) {
    //                if (isReceiveRepass()) {
    //                    sendToOthers(token);
    //                    return "REPASSED";
    //                } else {
    //                    sendToRepass(token);
    //                    return "ADDED";
    //                }
    //            } else {
    //                return "EXISTS";
    //            }
    //        } catch (Exception ex) {
    //            Server.logError(ex);
    //            return ex.getMessage();
    //        }
    //    }

    public String processBlock(String token) {
        try {
            if ((token = SPF.normalizeTokenFull(token)) == null) {
                return "INVALID";
            } else if (!isValidBlock(token)) {
                return "INVALID";
            } else if (Ignore.contains(token)) {
                return "IGNORED";
            } else if (isReceiveReject()) {
                return "REJECTED";
            } else if (isReceiveDrop()) {
                return "DROPPED";
            } else if (Block.containsExact(token)) {
                return "EXISTS";
            } else if (SPF.isGreen(token, false)) {
                return "GREEN";
                //            } else if (isReceiveRetain()) {
                //                if (addRetain(token)) {
                //                    return "RETAINED";
                //                } else {
                //                    return "DROPPED";
                //                }
            } else if (Block.addExact(token)) {
                if (isReceiveRepass()) {
                    sendToOthers(token);
                    return "REPASSED";
                } else {
                    sendToRepass(token);
                    return "ADDED";
                }
            } else {
                return "EXISTS";
            }
        } catch (Exception ex) {
            Server.logError(ex);
            return ex.getMessage();
        }
    }

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

    public static synchronized void load() {
        long time = System.currentTimeMillis();
        File file = new File("./data/peer.map");
        if (file.exists()) {
            try {
                HashMap<Object, Object> map;
                FileInputStream fileInputStream = new FileInputStream(file);
                try {
                    map = SerializationUtils.deserialize(fileInputStream);
                } finally {
                    fileInputStream.close();
                }
                for (Object key : map.keySet()) {
                    Object value = map.get(key);
                    if (key instanceof InetAddress) {
                        InetAddress address = (InetAddress) key;
                        Integer port = (Integer) value;
                        create(address.getHostAddress(), port);
                    } else if (key instanceof String) {
                        String address = (String) key;
                        if (value instanceof Integer) {
                            Integer port = (Integer) value;
                            create(address, port);
                        } else if (value instanceof Peer) {
                            Peer peer = (Peer) value;
                            if (peer.send == Send.DUNNO) {
                                // Obsoleto.
                                peer.send = Send.NEVER;
                            } else if (peer.send == Send.BLOCK) {
                                // Obsoleto.
                                peer.send = Send.ALWAYS;
                            }
                            if (peer.receive == Receive.CONFIRM) {
                                // Obsoleto.
                                peer.receive = Receive.REPUTATION;
                            }
                            if (peer.receive == Receive.RETAIN) {
                                // Obsoleto.
                                peer.receive = Receive.REPUTATION;
                            }
                            //                            if (peer.retainSet == null) {
                            //                                peer.retainSet = new TreeSet<String>();
                            //                            }
                            if (peer.limit == 0) {
                                peer.limit = 100;
                            }
                            if (peer.reputationMap2 == null) {
                                peer.reputationMap2 = new TreeMap<String, Binomial>();
                            }
                            if (peer.reputationMap != null) {
                                peer.reputationMap2.putAll(peer.reputationMap);
                                peer.reputationMap.clear();
                            }
                            MAP.put(address, peer);
                        }
                    }
                }
                Server.logLoad(time, file);
            } catch (Exception ex) {
                Server.logError(ex);
            }
        } else {
            try {
                if (Core.hasPeerConnection()) {
                    Peer matrix = Peer.create("matrix.spfbl.net", 9877);
                    matrix.setReceiveStatus(Receive.ACCEPT);
                    matrix.setSendStatus(Send.REPUTATION);
                    matrix.sendHELO();
                }
            } catch (ProcessException ex) {
                Server.logError(ex);
            }
        }
    }

    public boolean hasEmail() {
        return email != null;
    }

    public boolean hasFrequency() {
        return frequency != null;
    }

    public long getIdleTimeMillis() {
        if (last == 0) {
            return 0;
        } else {
            return System.currentTimeMillis() - last;
        }
    }

    public String getFrequencyLiteral() {
        if (hasFrequency()) {
            int frequencyInt = frequency.getMaximumInt();
            long idleTimeInt = getIdleTimeMillis();
            if (idleTimeInt > frequencyInt * 5 && idleTimeInt > 3600000) {
                return "DEAD";
            } else {
                char sinal = '~';
                if (frequencyInt < limit) {
                    frequencyInt = limit;
                    sinal = '<';
                } else if (idleTimeInt > frequencyInt * 3) {
                    sinal = '>';
                }
                if (frequencyInt >= 3600000) {
                    return sinal + ((frequencyInt / 3600000) + "h");
                } else if (frequencyInt >= 60000) {
                    return sinal + ((frequencyInt / 60000) + "min");
                } else if (frequencyInt >= 1000) {
                    return sinal + ((frequencyInt / 1000) + "s");
                } else {
                    return sinal + (frequencyInt + "ms");
                }
            }
        } else {
            return "UNDEFINED";
        }
    }

    private Float getInterval() {
        long current = System.currentTimeMillis();
        Float interval;
        if (last == 0) {
            interval = null;
        } else {
            interval = (float) (current - last);
        }
        last = current;
        return interval;
    }

    public void addNotification() {
        Float interval = getInterval();
        if (interval == null) {
            // Se no houver intervalo definido, fazer nada.
        } else if (frequency == null) {
            frequency = new NormalDistribution(interval);
        } else {
            frequency.addElement(interval);
        }
    }

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

    @Override
    public boolean equals(Object o) {
        if (o instanceof Peer) {
            Peer other = (Peer) o;
            return this.address.equals(other.address);
        } else {
            return false;
        }
    }

    @Override
    public int compareTo(Peer other) {
        if (other == null) {
            return -1;
        } else {
            return this.toString().compareTo(other.toString());
        }
    }

    @Override
    public String toString() {
        User user = getUser();
        if (user == null) {
            return address + ":" + port + (send == null ? "" : " " + send.name())
                    + (receive == null ? "" : " " + receive.name())
                    //                    + (retainSet == null ? "" : " " + retainSet.size())
                    + " " + getFrequencyLiteral() + " " + reputationMax + (email == null ? "" : " <" + email + ">");
        } else {
            return address + ":" + port + (send == null ? "" : " " + send.name())
                    + (receive == null ? "" : " " + receive.name())
                    //                    + (retainSet == null ? "" : " " + retainSet.size())
                    + " " + getFrequencyLiteral() + " " + reputationMax + " " + user.getContact();
        }
    }

    protected String setReputation(String key, String ham, String spam) {
        try {
            if (!SPF.isValidReputation(key)) {
                return "INVALID";
            } else if (Generic.containsGenericSoft(key)) {
                return "GENERIC";
            } else if (Domain.isOfficialTLD(key)) {
                return "RESERVED";
            } else if (Subnet.isReservedIP(key)) {
                return "RESERVED";
            } else if (isReceiveReject()) {
                return "REJECTED";
            } else if (isReceiveDrop()) {
                return "DROPPED";
            } else {
                //                if (reputationMap2 == null) {
                //                    reputationMap2 = new TreeMap<String,Binomial>();
                //                }
                int hamInt = Integer.parseInt(ham);
                int spamInt = Integer.parseInt(spam);
                //                int total = hamInt + spamInt;
                //                if (total > reputationMax) {
                //                    float proporcion = (float) reputationMax / total;
                //                    hamInt = (int) (hamInt * proporcion);
                //                    spamInt = (int) (spamInt * proporcion);
                //                }
                Binomial binomial;
                if (hamInt == 0 && spamInt == 0) {
                    binomial = dropReputation(key);
                    if (binomial == null) {
                        return "NOT FOUND";
                    } else {
                        CHANGED = true;
                        return "DROPPED";
                    }
                } else if ((binomial = getReputation(key)) == null) {
                    binomial = new Binomial(hamInt, spamInt);
                    putReputation(key, binomial);
                    if (Ignore.contains(key)) {
                        binomial.clear();
                        return "IGNORED";
                    } else {
                        return "ADDED";
                    }
                } else {
                    binomial.set(hamInt, spamInt);
                    if (Ignore.contains(key)) {
                        binomial.clear();
                        return "IGNORED";
                    } else {
                        return "UPDATED";
                    }
                }
            }
        } catch (Exception ex) {
            return "INVALID";
        }
    }

    public boolean isExpired7() {
        return System.currentTimeMillis() - last > 604800000 && System.currentTimeMillis() - create > 604800000;
    }

    public short getReputationMax() {
        return reputationMax;
    }

    private void refreshReputationMax() {
        reputationMax = (short) Math.max((int) (Core.getReputationLimit() * getCorrelacao()), 0);
        CHANGED = true;
    }

    public static void dropExpired() {
        String origin = null;
        for (Peer peer : getSet()) {
            long time = System.currentTimeMillis();
            if (peer.isExpired7()) {
                if (peer.drop()) {
                    Server.log(time, Core.Level.INFO, "PEERH", origin, peer.getAddress(), "EXPIRED");
                }
            } else {
                try {
                    peer.refreshReputationMax();
                    TreeMap<String, Binomial> reputationMap = peer.getReputationMap();
                    for (String key : reputationMap.keySet()) {
                        time = System.currentTimeMillis();
                        Binomial binomial = reputationMap.get(key);
                        if (binomial.isExpired3()) {
                            binomial = peer.dropReputation(key);
                            if (binomial != null) {
                                Server.log(time, Core.Level.INFO, "PEERR", peer.getAddress(), key, "EXPIRED");
                            }
                        }
                    }
                } catch (Exception ex) {
                    Server.logError(ex);
                }
            }
        }
    }

    //    public static TreeSet<String> clearAllReputation(String key) {
    //        TreeSet<String> clearSet = new TreeSet<String>();
    //        for (Peer peer : getSet()) {
    //            for (String token : peer.getAllReputations(key)) {
    //                if (peer.clearReputation(token)) {
    //                    clearSet.add(token);
    //                }
    //            }
    //        }
    //        return clearSet;
    //    }

    private synchronized boolean containsReputationExact(String key) {
        if (reputationMap2 == null) {
            return false;
        } else {
            return reputationMap2.containsKey(key);
        }
    }

    private synchronized Set<String> subSet(String begin, String end) {
        TreeSet<String> subSet = new TreeSet<String>();
        if (reputationMap2 != null) {
            NavigableMap<String, Binomial> subMap = reputationMap2.subMap(begin, false, end, false);
            subSet.addAll(subMap.keySet());
        }
        return subSet;
    }

    private TreeSet<String> getAllReputations(String value) {
        TreeSet<String> blockSet = new TreeSet<String>();
        if (Subnet.isValidIP(value)) {
            String ip = Subnet.normalizeIP(value);
            if (containsReputationExact(ip)) {
                blockSet.add(ip);
            }
        } else if (Subnet.isValidCIDR(value)) {
            String cidr = Subnet.normalizeCIDR(value);
            for (String ip : subSet("0", ":")) {
                if (Subnet.containsIP(cidr, ip)) {
                    blockSet.add(ip);
                }
            }
            for (String ip : subSet("a", "g")) {
                if (SubnetIPv6.containsIP(cidr, ip)) {
                    blockSet.add(ip);
                }
            }
        } else if (value.startsWith(".")) {
            String hostname = value;
            for (String key : subSet(".", "/")) {
                if (key.endsWith(hostname)) {
                    blockSet.add(key);
                }
            }
            for (String mx : subSet("@", "A")) {
                String hostKey = '.' + mx.substring(1);
                if (hostKey.endsWith(hostname)) {
                    blockSet.add(hostKey);
                }
            }
        } else if (containsReputationExact(value)) {
            blockSet.add(value);
        }
        return blockSet;
    }

    private synchronized Binomial dropReputation(String key) {
        if (reputationMap2 == null) {
            return null;
        } else {
            return reputationMap2.remove(key);
        }
    }

    private synchronized Binomial putReputation(String key, Binomial binomial) {
        if (reputationMap2 == null) {
            reputationMap2 = new TreeMap<String, Binomial>();
        }
        return reputationMap2.put(key, binomial);
    }

    public synchronized Binomial getReputation(String key) {
        if (reputationMap2 == null) {
            return null;
        } else {
            return reputationMap2.get(key);
        }
    }

    private boolean clearReputation(String key) {
        Binomial binomial = getReputation(key);
        if (binomial == null) {
            return false;
        } else {
            return binomial.clear();
        }
    }

    /**
     * Classe que representa a distribuio binomial entre HAM e SPAM.
     */
    public static final class Binomial implements Serializable {

        private static final long serialVersionUID = 1L;

        private int ham; // Quantidade total de HAM em sete dias.
        private int spam; // Quantidade total de SPAM em sete dias
        private long last = System.currentTimeMillis();
        private final Status status;

        public Binomial(int ham, int spam) throws ProcessException {
            this.status = null;
            set(ham, spam);
        }

        public Binomial(Status status) throws ProcessException {
            this.status = status;
            set(0, 0);
        }

        public synchronized void set(int ham, int spam) throws ProcessException {
            if (ham < 0) {
                throw new ProcessException("INVALID HAM VALUE");
            } else if (spam < 0) {
                throw new ProcessException("INVALID SPAM VALUE");
            } else if (this.ham != ham || this.spam != spam) {
                this.ham = ham;
                this.spam = spam;
                this.last = System.currentTimeMillis();
                CHANGED = true;
            }
        }

        public int getSPAM() {
            return spam;
        }

        public int getHAM() {
            return ham;
        }

        public int getTotalSize() {
            return ham + spam;
        }

        public Status getStatus() {
            return status;
        }

        public synchronized boolean clear() {
            if (spam > 0) {
                this.ham += spam;
                this.spam = 0;
                CHANGED = true;
                return true;
            } else {
                return false;
            }
        }

        public boolean isExpired3() {
            return System.currentTimeMillis() - last > 259200000;
        }

        public boolean isExpired7() {
            return System.currentTimeMillis() - last > 604800000;
        }

        public boolean hasLastUpdate() {
            return last > 0;
        }

        public synchronized float getSpamProbability() {
            if (ham + spam == 0) {
                return 0.0f;
            } else {
                return (float) spam / (float) (ham + spam);
            }
        }

        @Override
        public String toString() {
            return Float.toString(getSpamProbability());
        }
    }
}