Java tutorial
/* * 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 com.sun.mail.smtp.SMTPAddressFailedException; import com.sun.mail.util.MailConnectException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; import javax.naming.NameNotFoundException; import javax.naming.NamingException; import net.spfbl.data.Block; import net.spfbl.data.Generic; import net.spfbl.data.NoReply; import net.spfbl.data.Provider; import net.spfbl.data.Trap; import net.spfbl.data.White; import net.spfbl.http.ServerHTTP; import net.spfbl.spf.SPF; import net.spfbl.spf.SPF.Distribution; import net.spfbl.whois.Domain; import net.spfbl.whois.Subnet; import net.spfbl.whois.SubnetIPv4; import net.spfbl.whois.SubnetIPv6; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.SerializationUtils; /** * Representa um usurio do sistema. * * @author Leandro Carlos Rodrigues <leandro@spfbl.net> */ public class User implements Serializable, Comparable<User> { private static final long serialVersionUID = 1L; private final String email; private String name; private Locale locale; private boolean trusted = false; private boolean local = false; private boolean usingHeader = false; /** * Atributos para OTP. */ private String otp_secret = null; // Chave oficial. private String otp_transition = null; // Chave de transio. private byte otp_fail = 0; private Integer otp_sucess = null; private long otp_last = 0; private User(String email, String name) throws ProcessException { if (Domain.isEmail(email) && simplify(name) != null) { this.email = email.toLowerCase(); this.name = simplify(name); this.locale = Core.getDefaultLocale(email); } else { throw new ProcessException("INVALID USER"); } } public void setName(String name) throws ProcessException { if (simplify(name) != null && !this.name.equals(simplify(name))) { this.name = simplify(name); CHANGED = true; } else { throw new ProcessException("INVALID NAME"); } } public boolean setTrusted(boolean trusted) { if (this.trusted == trusted) { return false; } else { this.trusted = trusted; return CHANGED = true; } } public boolean setLocal(boolean local) { if (this.local == local) { return false; } else { this.local = local; return CHANGED = true; } } /** * Change locale of user. * @param token locale pattern. * @return true if value was changed.LocaleUtils.toLocale(language); */ public boolean setLocale(String token) { if (token == null) { return false; } else { Locale newLocale = LocaleUtils.toLocale(token); if (newLocale == null) { return false; } else if (newLocale.equals(this.locale)) { return false; } else { this.locale = newLocale; return CHANGED = true; } } } public String getEmail() { return email; } public InternetAddress getAdminInternetAddress() { try { return new InternetAddress(email, name); } catch (UnsupportedEncodingException ex) { return null; } } public String getDomain() { int index = email.indexOf('@') + 1; return email.substring(index); } public boolean isTrusted() { return trusted; } public boolean isLocal() { return local; } public boolean isUsingHeader() { return usingHeader; } public boolean isPostmaster() { return email.startsWith("postmaster@"); } public boolean isSameDomain(String address) { if (address == null) { return false; } else { int index1 = email.indexOf('@') + 1; int index2 = address.indexOf('@') + 1; String domain1 = email.substring(index1); String domain2 = address.substring(index2); return domain1.equals(domain2); } } public boolean isEmail(String email) { return this.email.equals(email); } public boolean hasSecretOTP() { return otp_secret != null; } public boolean hasTransitionOTP() { return otp_transition != null; } public String newSecretOTP() { CHANGED = true; return otp_transition = Core.generateSecretOTP(); } public long getFailTime() { long thresholdTime = (long) Math.pow(2, otp_fail); long idleTime = System.currentTimeMillis() - otp_last; if (idleTime < 1000) { return 1000; } else { return thresholdTime - idleTime; } } public boolean tooManyFails() { long thresholdTime = (long) Math.pow(2, otp_fail); long idleTime = System.currentTimeMillis() - otp_last; if (idleTime < 1000) { return false; } else { return thresholdTime > idleTime; } } public boolean isValidOTP(Integer code) { if (code == null) { return false; } else if (code.equals(otp_sucess)) { return false; } else if (Core.isValidOTP(otp_transition, code)) { otp_secret = otp_transition; otp_transition = null; otp_fail = 0; otp_sucess = code; otp_last = System.currentTimeMillis(); CHANGED = true; return true; } else if (Core.isValidOTP(otp_secret, code)) { otp_transition = null; otp_fail = 0; otp_sucess = code; otp_last = System.currentTimeMillis(); CHANGED = true; return true; } else if (otp_fail < Byte.MAX_VALUE) { otp_fail++; otp_last = System.currentTimeMillis(); CHANGED = true; return false; } else { otp_last = System.currentTimeMillis(); CHANGED = true; return false; } } public String getName() { return name; } public String getContact() { return name + " <" + email + ">"; } public static Locale getLocale(String address) { if (address == null) { return null; } else { address = address.toLowerCase(); User user = User.get(address); if (user == null) { int index = address.indexOf('@'); String postmaster = "postmaster" + address.substring(index); user = User.get(postmaster); } if (user == null) { return Core.getDefaultLocale(address); } else { return user.getLocale(); } } } public Locale getLocale() { return locale; } public InternetAddress getInternetAddress() throws UnsupportedEncodingException { return new InternetAddress(email, name); } public InternetAddress[] getInternetAddresses() throws UnsupportedEncodingException { InternetAddress[] internetAddresses = new InternetAddress[1]; internetAddresses[0] = getInternetAddress(); return internetAddresses; } private static String simplify(String text) { if (text == null) { return null; } else { char[] charArray = text.toCharArray(); for (int i = 0; i < charArray.length; i++) { char character = charArray[i]; if (character == '\n') { charArray[i] = '\n'; } else if (character == '') { charArray[i] = '"'; } else if (character == '?') { charArray[i] = '"'; } else if (Character.isISOControl(character)) { charArray[i] = ' '; } } text = new String(charArray); while (text.contains(" ")) { text = text.replace(" ", " "); } while (text.contains(" \n")) { text = text.replace(" \n", "\n"); } while (text.contains("\n ")) { text = text.replace("\n ", "\n"); } text = text.trim(); if (text.length() == 0) { return null; } else { return text; } } } public synchronized boolean dropQuery(long time) { if (queryMap == null) { return false; } else { return queryMap.remove(time) != null; } } public synchronized Set<Long> headSet(long threshold) { if (queryMap == null) { return new TreeSet<Long>(); } else { TreeSet<Long> set = new TreeSet<Long>(); set.addAll(queryMap.headMap(threshold).keySet()); return set; } } private void dropExpiredQuery() { long threshold = System.currentTimeMillis() - 604800000; for (long time : headSet(threshold)) { Query query = getQuery(time); if (query != null) { query.storeDB(time); } if (dropQuery(time)) { CHANGED = true; } } } private static final int QUERY_MAX = 4096; private synchronized void hairCutQuery() { if (queryMap != null && queryMap.size() > QUERY_MAX) { Long time = 0L; Query query; do { if ((time = queryMap.higherKey(time)) == null) { break; } else if ((query = queryMap.get(time)) != null && !query.isHolding()) { if (queryMap.remove(time) != null) { CHANGED = true; } } } while (queryMap.size() > QUERY_MAX); } } public static void dropAllExpiredQuery() { for (User user : getSet()) { user.dropExpiredQuery(); user.hairCutQuery(); } } /** * Mapa de usurio com busca de hash O(1). */ private static final HashMap<String, User> MAP = new HashMap<String, User>(); /** * Flag que indica se o cache foi modificado. */ private static boolean CHANGED = false; public synchronized static User create(String email, String name) throws ProcessException { if (MAP.containsKey(email)) { return null; } else { User user = new User(email, name); MAP.put(email, user); CHANGED = true; return user; } } public synchronized static TreeSet<User> getSet() { TreeSet<User> userSet = new TreeSet<User>(); userSet.addAll(MAP.values()); return userSet; } public synchronized static User drop(String email) { User user = MAP.remove(email); if (user != null) { CHANGED = true; } return user; } public static TreeSet<User> dropAll() { TreeSet<User> userSet = new TreeSet<User>(); for (User user : getSet()) { String email = user.getEmail(); user = drop(email); if (email != null) { userSet.add(user); } } return userSet; } public synchronized static User get(String email) { if (email == null) { return null; } else { return MAP.get(email); } } public static boolean exists(String email) { if (email == null) { return false; } else { return MAP.containsKey(email); } } public static boolean exists(String... emailSet) { if (emailSet == null) { return false; } else { for (String email : emailSet) { if (exists(email)) { return true; } } return false; } } public static synchronized HashMap<String, User> getMap() { HashMap<String, User> map = new HashMap<String, User>(); map.putAll(MAP); return map; } public synchronized static void store() { if (CHANGED) { try { storeDB(); // Server.logTrace("storing user.map"); long time = System.currentTimeMillis(); HashMap<String, User> map = getMap(); File file = new File("./data/user.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/user.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 User) { User user = (User) value; if (user.locale == null) { user.locale = Core.getDefaultLocale(user.email); } for (long time2 : user.getTimeSet()) { Query query = user.getQuery(time2); if (query.CHANGED == null) { query.CHANGED = new BinarySemaphore(!query.STORED); } } MAP.put(key, user); } } Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } } @Override public int hashCode() { return email.hashCode(); } @Override public boolean equals(Object o) { if (o instanceof User) { User other = (User) o; return this.email.equals(other.email); } else { return false; } } @Override public int compareTo(User other) { if (other == null) { return -1; } else { return this.toString().compareTo(other.toString()); } } @Override public String toString() { if (locale == null) { return name + " <" + email + ">"; } else { return name + " <" + email + "> " + locale; } } /** * Registro de consultas. */ private TreeMap<Long, Query> queryMap = null; public User.Query addQuery(long time, Client client, String ip, String helo, String hostname, String sender, String qualifier, String recipient, TreeSet<String> tokenSet, String result) { SPF.Qualifier qualifierEnum; try { qualifierEnum = SPF.Qualifier.valueOf(qualifier); } catch (Exception ex) { qualifierEnum = null; } if (client == null) { return addQuery(time, this.getDomain(), ip, helo, hostname, sender, qualifierEnum, recipient, tokenSet, result); } else { return addQuery(time, client.getDomain(), ip, helo, hostname, sender, qualifierEnum, recipient, tokenSet, result); } } public User.Query addQuery(long time, String client, String ip, String helo, String hostname, String sender, SPF.Qualifier qualifier, String recipient, TreeSet<String> tokenSet, String result) { try { Query query = new Query(client, ip, helo, hostname, sender, qualifier, recipient, tokenSet, result); putQuery(time, query); storeDB(time, query); return query; } catch (ProcessException ex) { Server.logError(ex); return null; } } public static final int QUERY_MAX_ROWS = 512; // public TreeSet<Long> getQueryKeySet( // Long begin, String filter // ) { // // Busca convencional. // TreeSet<Long> keySet = new TreeSet<Long>(); // if (filter == null || filter.length() == 0) { // keySet.addAll(getTimeSet()); // } else { // for (Long time : getTimeSet()) { // Query query = getQuery(time); // if (query != null && query.match(filter)) { // keySet.add(time); // } // } // } // if (begin != null) { // TreeSet<Long> tailSet = new TreeSet<Long>(); // tailSet.addAll(keySet.tailSet(begin)); // keySet.removeAll(tailSet); // } // return keySet; // } public TreeMap<Long, Query> getQueryMap(Long begin, String filter) { TreeMap<Long, Query> queryLocalMap = getQueryHeadMap(begin); Connection connection = Core.poolConnectionMySQL(); try { if (connection != null) { try { String ipParam = Subnet.isValidIP(filter) ? Subnet.normalizeIP(filter) : null; String emailParam = Domain.isValidEmail(filter) ? filter.toLowerCase() : null; String command = "SELECT * FROM spfbl.user_query\n" + "WHERE user = '" + getEmail() + "'\n" + (begin == null ? "" : "AND time <= " + begin + "\n") + ("rejeitada".equals(filter) ? "AND result " + "IN('BLOCK','REJECT')\n" : "") + (ipParam == null ? "" : "AND ip = '" + ipParam + "'\n") + (emailParam == null ? "" : "AND '" + emailParam + "' " + "IN(sender, mailFrom, replyto, recipient)\n") + "ORDER BY time DESC\n" + "LIMIT " + (QUERY_MAX_ROWS + 1); Statement statement = connection.createStatement(); try { ResultSet rs = statement.executeQuery(command); while (rs.next()) { try { long time = rs.getLong("time"); Query query = queryLocalMap.get(time); if (query == null) { query = new Query(rs); queryLocalMap.put(time, query); } } catch (Exception ex) { Server.logError(ex); } } } finally { statement.close(); } } catch (SQLException ex) { Server.logError(ex); } } } finally { Core.offerConnectionMySQL(connection); } TreeMap<Long, Query> resultMap = new TreeMap<Long, Query>(); while (resultMap.size() < (QUERY_MAX_ROWS + 1)) { Entry<Long, Query> entry = queryLocalMap.pollLastEntry(); if (entry == null) { break; } else { long time = entry.getKey(); Query query = entry.getValue(); if (filter == null) { resultMap.put(time, query); } else if (filter.length() == 0) { resultMap.put(time, query); } else if (query.match(filter)) { resultMap.put(time, query); } } } return resultMap; } public void setResult(long time, String result) { if (result != null) { Query query = getQuerySafe(time); if (query != null && query.setResult(result)) { storeDB(time, query); } } } public Query getQuerySafe(long time) { Query query = getQuery(time); if (query == null && Core.hasMySQL()) { long time2 = System.currentTimeMillis(); String command = "SELECT * FROM spfbl.user_query " + "WHERE time = " + time; Connection connection = Core.poolConnectionMySQL(); try { if (connection != null) { Statement statement = connection.createStatement(); try { try { ResultSet rs = statement.executeQuery(command); if (rs.next()) { query = new Query(rs); Server.logMySQL(time2, command, "FOUND"); } else { Server.logMySQL(time2, command, "NOT FOUND"); } } finally { statement.close(); } } catch (SQLException ex) { Server.logMySQL(time2, command, ex); } } } catch (SQLException ex) { Server.logError(ex); } finally { Core.offerConnectionMySQL(connection); } } return query; } public synchronized Query getQuery(long time) { if (queryMap == null) { return null; } else { return queryMap.get(time); } } public synchronized TreeSet<Long> getTimeSet() { if (queryMap == null) { return new TreeSet<Long>(); } else { TreeSet<Long> timeSet = new TreeSet<Long>(); timeSet.addAll(queryMap.keySet()); return timeSet; } } public synchronized TreeSet<Long> getTimeSet(long begin, long end) { if (queryMap == null) { return new TreeSet<Long>(); } else { TreeSet<Long> timeSet = new TreeSet<Long>(); timeSet.addAll(queryMap.subMap(begin, end).keySet()); return timeSet; } } public synchronized TreeMap<Long, Query> getQueryHeadMap(Long begin) { if (queryMap == null) { return new TreeMap<Long, Query>(); } else if (begin == null) { TreeMap<Long, Query> resultMap = new TreeMap<Long, Query>(); resultMap.putAll(queryMap); return resultMap; } else { TreeMap<Long, Query> resultMap = new TreeMap<Long, Query>(); resultMap.putAll(queryMap.headMap(begin)); return resultMap; } } private synchronized void putQuery(long time, Query query) { if (queryMap == null) { queryMap = new TreeMap<Long, Query>(); } queryMap.put(time, query); CHANGED = true; } public String blockByMessageID(String messageID) { if (messageID == null || messageID.length() == 0) { return "INVALID MESSAGE"; } else { for (long time : getTimeSet().descendingSet()) { Query query = getQuerySafe(time); if (query != null && query.isMessage(messageID)) { if (query.isWhiteSender() && query.isGreen()) { if (query.complain(time)) { return "COMPLAINED " + query.getTokenSet(); } else { return "ALREADY COMPLAINED"; } } else if (query.blockSender(time)) { return "BLOCKED " + query.getBlockSender(); } else { return "ALREADY BLOCKED"; } } } return "MESSAGE NOT FOUND"; } } public String whiteByMessageID(String messageID) { if (messageID == null || messageID.length() == 0) { return "INVALID MESSAGE"; } else { for (long time : getTimeSet().descendingSet()) { Query query = getQuerySafe(time); if (query != null && query.isMessage(messageID)) { String block = query.getBlock(); if (block == null) { Situation situation = query.getSituation(true); if (situation == Situation.ORIGIN) { return "INVALID SENDER"; } else if (query.white(time, situation)) { switch (situation) { case IP: return "ADDED " + query.getSenderSimplified(false, true) + ";" + query.getIP(); case ZONE: return "ADDED " + query.getSenderSimplified(false, true) + ";" + query.getOriginDomain(false); case AUTHENTIC: return "ADDED " + query.getSenderSimplified(false, true) + ";PASS"; default: return "ERROR: FATAL"; } } else { return "ALREADY EXISTS"; } } else { return "BLOCKED AS " + block; } } } return "NOT FOUND"; } } public enum Situation { NONE, ORIGIN, IP, ZONE, AUTHENTIC, SAME, DOMAIN, RECIPIENT, ALL } public static boolean isExpiredHOLD(long time) { long expireTime = Core.getDeferTimeHOLD() * 60000L; long thresholdTime = System.currentTimeMillis() - expireTime; return time < thresholdTime; } public static void sendHoldingWarning() { for (User user : getSet()) { if (user.isUsingHeader()) { HashSet<String> keySet = new HashSet<String>(); TreeSet<Long> timeSet = user.getTimeSet(); long deferTimeYELLOW = Core.getDeferTimeYELLOW() * 60000L; long deferTimeRED = Core.getDeferTimeRED() * 60000L; long deferTimeHOLD = Core.getDeferTimeHOLD() * 60000L; long timeEnd = System.currentTimeMillis() - deferTimeYELLOW; long timeMiddle = System.currentTimeMillis() - deferTimeRED; long timeBegin = System.currentTimeMillis() - deferTimeHOLD; int count = 0; for (long time : timeSet.subSet(timeBegin, timeEnd)) { Query query = user.getQuery(time); if (query != null && query.hasSubject() && query.isNotAdvisedLocal() && query.isResult("HOLD") && keySet.add(query.getComplainKey())) { if (query.isHoldingFull()) { if (query.adviseSenderHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } else if (!query.isSenderAdvised() && !query.isPass() && query.adviseAdminHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } else if (!query.isSenderAdvised() && query.adviseRecipientHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } else if (time < timeMiddle && !query.isPass() && query.adviseAdminHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } else if (time < timeMiddle && query.adviseRecipientHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } else if (time < timeMiddle && query.adviseAdminHOLD(time)) { CHANGED = true; Server.logDebug("retention warning sent by e-mail."); } } if (++count > 1024) { break; } } } } } } public static void sendSuspectWarning() { for (User user : getSet()) { if (user.isUsingHeader()) { HashSet<String> keySet = new HashSet<String>(); TreeSet<Long> timeSet = user.getTimeSet(); long deferTimeYELLOW = Core.getDeferTimeYELLOW() * 60000L; long deferTimeRED = Core.getDeferTimeRED() * 60000L; long timeEnd = System.currentTimeMillis() - deferTimeYELLOW; long timeBegin = System.currentTimeMillis() - deferTimeRED; int count = 0; for (long time : timeSet.subSet(timeBegin, timeEnd)) { Query query = user.getQuery(time); if (query != null && query.hasSubject() && query.hasMessageID() && query.isNotAdvised() && query.isResult("ACCEPT") && keySet.add(query.getComplainKey()) && query.isSuspectFull()) { if (query.adviseRecipientSPAM(time)) { CHANGED = true; Server.logDebug("suspect warning sent by e-mail."); } if (++count > 1024) { break; } } } } } } // public static void sendBlockedWarning() { // for (User user : getSet()) { // if (user.isUsingHeader()) { // HashSet<String> keySet = new HashSet<String>(); // TreeSet<Long> timeSet = user.getTimeSet(); // long deferTimeYELLOW = Core.getDeferTimeYELLOW() * 60000L; // long deferTimeRED = Core.getDeferTimeRED() * 60000L; // long timeEnd = System.currentTimeMillis() - deferTimeYELLOW; // long timeBegin = System.currentTimeMillis() - deferTimeRED; // int count = 0; // for (long time : timeSet.subSet(timeBegin, timeEnd)) { // Query query = user.getQuery(time); // if (query != null && // query.isNotAdvised() && // query.isResult("BLOCK") && // keySet.add(query.getComplainKey()) // ) { // if (query.adviseSenderBLOCK(time)) { // CHANGED = true; // Server.logDebug("reject warning sent by e-mail."); // if (++count > 32) { // break; // } // } // } // } // } // } // } public static void sendBlockedWarning() { for (User user : getSet()) { long deferTimeYELLOW = Core.getDeferTimeYELLOW() * 60000L; long deferTimeRED = Core.getDeferTimeRED() * 60000L; long timeEnd = System.currentTimeMillis() - deferTimeYELLOW; long timeBegin = System.currentTimeMillis() - deferTimeRED; for (long time : user.getTimeSet(timeBegin, timeEnd)) { Query query = user.getQuery(time); if (query != null && query.isResult("BLOCK")) { if (query.adviseSenderBLOCK(time)) { Server.logDebug("reject warning sent by e-mail."); } if (query.storeDB(time)) { // Drop from memory because // it was stored in MySQL. user.dropQuery(time); } } } } } public boolean sendTOTP() { if (!Core.hasOutputSMTP()) { return false; } else if (!Core.hasAdminEmail()) { Server.logError("no admin e-mail to send TOTP."); return false; } else if (!Core.hasAdminEmail()) { return false; } else if (NoReply.contains(getEmail(), true)) { return false; } else { return ServerHTTP.enviarOTP(locale, this); } } private static final String MYSQL_STORE_COMMAND = "INSERT INTO user_query " + "(time, user, client, ip, helo, hostname, " + "sender, qualifier, recipient, tokenSet, complainKey, " + "result, mailFrom, replyto, subject, " + "messageID, unsubscribe, linkMap, malware, " + "adminAdvised, senderAdvised, recipientAdvised)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n" + "ON DUPLICATE KEY UPDATE result = ?, mailFrom = ?, " + "replyto = ?, subject = ?, messageID = ?, " + "unsubscribe = ?, linkMap = ?, malware = ?, " + "adminAdvised = ?, senderAdvised = ?, " + "recipientAdvised = ?"; private static void storeDB() { try { long time2 = System.currentTimeMillis(); Connection connection = Core.poolConnectionMySQL(); try { if (connection != null) { PreparedStatement statement = connection.prepareStatement(MYSQL_STORE_COMMAND); try { connection.setAutoCommit(true); for (User user : getSet()) { for (long time : user.getTimeSet()) { Query query = user.getQuery(time); if (query != null) { query.storeDB(statement, time); } } } Server.logMySQL(time2, "user_query stored"); } finally { connection.setAutoCommit(false); statement.close(); } } } finally { Core.offerConnectionMySQL(connection); } } catch (SQLException ex) { Server.logError(ex); } } private static StoreThread STORE_THREAD = null; public static synchronized void storeDB(long time, Query query) { if (Core.hasMySQL()) { if (STORE_THREAD == null) { STORE_THREAD = new StoreThread(); } STORE_THREAD.put(time, query); } } protected static synchronized void interrupt() { if (STORE_THREAD != null) { STORE_THREAD.interrupt(); } } private static class StoreThread extends Thread { private final TreeMap<Long, Query> QUEUE = new TreeMap<Long, Query>(); private Connection CONNECTION = null; private boolean run = true; private synchronized void closeConnection() { if (CONNECTION != null) { try { CONNECTION.close(); CONNECTION = null; Server.logMySQL("connection closed."); } catch (SQLException ex) { Server.logError(ex); } } } private synchronized Connection getConnection() { if (CONNECTION == null) { return CONNECTION = Core.getConnectionMySQL(); } else { return CONNECTION; } } private synchronized Entry<Long, Query> pollFirstEntry() { return QUEUE.pollFirstEntry(); } private synchronized Query put(long time, Query query) { Query previous = QUEUE.put(time, query); if (isAlive()) { notify(); } else { start(); } return previous; } private synchronized boolean continueRun() { return run; } @Override public synchronized void interrupt() { run = false; notify(); } public synchronized void waitNotify(long time) { try { if (run) { wait(time); } } catch (InterruptedException ex) { Server.logError(ex); } } public synchronized void waitNotify() { try { if (run) { wait(); } } catch (InterruptedException ex) { Server.logError(ex); } } @Override public void run() { Thread.currentThread().setName("USRTHREAD"); while (continueRun()) { Entry<Long, Query> entry = pollFirstEntry(); if (entry == null) { waitNotify(60000); entry = pollFirstEntry(); if (entry == null) { closeConnection(); waitNotify(); } } if (entry != null) { long time = entry.getKey(); Query query = entry.getValue(); query.waitHeader(); Connection connection = getConnection(); if (connection == null) { put(time, query); } else { try { PreparedStatement statement = connection.prepareStatement(MYSQL_STORE_COMMAND); query.storeDB(statement, time); } catch (SQLException ex) { Server.logError(ex); } } } } } } public class Query implements Serializable { private static final long serialVersionUID = 1L; private String client; private String ip; private String helo; private String hostname = null; private String sender; private SPF.Qualifier qualifier; private String recipient; private final TreeSet<String> tokenSet = new TreeSet<String>(); private String result; private String from = null; private String replyto = null; private String subject = null; private String messageID = null; private URL unsubscribe = null; private TreeMap<String, Boolean> linkMap = null; private String malware = null; private boolean adminAdvised = false; private boolean senderAdvised = false; private boolean recipientAdvised = false; private boolean STORED = false; // Obsoleto. private BinarySemaphore CHANGED; // Mudar para final depois da transio. // private BinarySemaphore HEADER; // Sinaliza se j processou o cabealho. private Query(ResultSet rs) throws SQLException { this.client = rs.getString("client"); this.ip = rs.getString("ip"); this.helo = rs.getString("helo"); this.hostname = rs.getString("hostname"); this.sender = rs.getString("sender"); this.qualifier = SPF.Qualifier.get(rs.getString("qualifier")); this.recipient = rs.getString("recipient"); this.loadTokenSet(rs.getString("tokenSet")); this.result = rs.getString("result"); this.from = rs.getString("mailFrom"); this.replyto = rs.getString("replyto"); this.subject = rs.getString("subject"); this.messageID = rs.getString("messageID"); this.unsubscribe = Core.getURL(rs.getString("unsubscribe")); this.loadLinkMap(rs.getString("linkMap")); this.malware = rs.getString("malware"); this.adminAdvised = rs.getBoolean("adminAdvised"); this.senderAdvised = rs.getBoolean("senderAdvised"); this.recipientAdvised = rs.getBoolean("recipientAdvised"); this.STORED = true; this.CHANGED = new BinarySemaphore(false); } private boolean loadTokenSet(String text) { TreeSet<String> set = Core.getTreeSet(text, ";"); if (set == null) { return false; } else { tokenSet.addAll(set); return true; } } private boolean loadLinkMap(String text) { TreeMap<String, Boolean> map = Core.getTreeMapBoolean(text, ";"); if (map == null) { return false; } else if (linkMap == null) { linkMap = map; return true; } else { linkMap.putAll(map); return true; } } private boolean storeDB(long time) { try { Connection connection = Core.poolConnectionMySQL(); try { if (connection == null) { return false; } else { PreparedStatement statement = connection.prepareStatement(MYSQL_STORE_COMMAND); try { connection.setAutoCommit(true); return storeDB(statement, time); } finally { connection.setAutoCommit(false); statement.close(); } } } finally { Core.offerConnectionMySQL(connection); } } catch (SQLException ex) { Server.logError(ex); return false; } } private boolean storeDB(PreparedStatement statement, long time) { if (this.CHANGED.acquireIf(true)) { try { statement.setLong(1, time); statement.setString(2, getEmail()); statement.setString(3, client); statement.setString(4, ip); statement.setString(5, helo); statement.setString(6, hostname); statement.setString(7, sender); statement.setString(8, SPF.Qualifier.name(qualifier)); statement.setString(9, recipient); statement.setString(10, Core.getSequence(tokenSet, ";")); statement.setString(11, getComplainKey()); statement.setString(12, result); statement.setString(13, from); statement.setString(14, replyto); statement.setString(15, subject); statement.setString(16, messageID); statement.setString(17, getUnsubscribeString()); statement.setString(18, Core.getSequence(linkMap, ";")); statement.setString(19, malware); statement.setBoolean(20, adminAdvised); statement.setBoolean(21, senderAdvised); statement.setBoolean(22, recipientAdvised); statement.setString(23, result); statement.setString(24, from); statement.setString(25, replyto); statement.setString(26, subject); statement.setString(27, messageID); statement.setString(28, getUnsubscribeString()); statement.setString(29, Core.getSequence(linkMap, ";")); statement.setString(30, malware); statement.setBoolean(31, adminAdvised); statement.setBoolean(32, senderAdvised); statement.setBoolean(33, recipientAdvised); int update = statement.executeUpdate(); this.STORED = true; this.CHANGED.release(false); User.CHANGED = true; if (update == 0) { Server.logMySQL(time, statement, "NOT UPDATED"); } else { Server.logMySQL(time, statement, "UPDATED"); } return true; } catch (SQLException ex) { this.STORED = false; this.CHANGED.release(true); Server.logMySQL(time, statement, ex); return false; } } else { return true; } } private Query(String client, String ip, String helo, String hostname, String sender, SPF.Qualifier qualifier, String recipient, TreeSet<String> tokenSet, String result) throws ProcessException { if (!Domain.isHostname(client)) { throw new ProcessException("INVALID CLIENT"); } else if (!Subnet.isValidIP(ip)) { throw new ProcessException("INVALID IP"); } else if (sender != null && !sender.contains("@")) { throw new ProcessException("INVALID SENDER"); } else if (recipient != null && !Domain.isEmail(recipient)) { throw new ProcessException("INVALID RECIPIENT"); } else if (tokenSet == null) { throw new ProcessException("INVALID TOKEN SET"); } else if (result == null) { throw new ProcessException("INVALID RESULT"); } else { this.client = Domain.normalizeHostname(client, false); this.ip = Subnet.normalizeIP(ip); this.helo = helo == null ? null : helo.toLowerCase(); this.hostname = Domain.normalizeHostname(hostname, false); this.hostname = this.hostname == null ? "" : this.hostname; this.sender = sender; this.qualifier = qualifier; this.recipient = recipient; this.tokenSet.addAll(tokenSet); this.result = result; this.STORED = false; this.CHANGED = new BinarySemaphore(true); // this.HEADER = new BinarySemaphore(false); User.CHANGED = true; } } public String getClient() { return client; } public TreeSet<String> getClientEmailSet() { TreeSet<String> emailSet = new TreeSet<String>(); for (Client clientLocal : Client.getClientSet(client)) { String email = clientLocal.getEmail(); if (email != null) { emailSet.add(email); } } return emailSet; } public String getIP() { return ip; } public String getHELO() { if (helo == null) { return ""; } else { return helo; } } public String getOrigin(boolean pontuacao) { String host = getValidHostname(); if (host == null) { return ip; } else { return Domain.normalizeHostname(host, pontuacao); } } public String getOriginDomain(boolean pontuacao) { String host = getValidHostname(); if (host == null) { host = helo; } try { return Domain.extractDomain(host, pontuacao); } catch (ProcessException ex) { return null; } } public String getUnblockURL() throws ProcessException { return Core.getUnblockURL(getEmail(), getIP(), getMailFrom(), getValidHostname(), getRecipient()); } public TreeSet<String> getSenderMXDomainSet() throws NamingException { String host = getSenderHostname(false); TreeSet<String> mxSet = new TreeSet<String>(); if (host != null) { for (String mx : Reverse.getMXSet(host)) { try { String domain = Domain.extractDomain(mx, false); if (domain != null) { mxSet.add(domain); } } catch (ProcessException ex) { } } } return mxSet; } public ArrayList<String> getSenderMXSet() { try { return Reverse.getMXSet(getSenderHostname(false)); } catch (NamingException ex) { return null; } } public String getSenderHostname(boolean pontuacao) { String trueSender = getSender(); if (trueSender == null) { return null; } else { int index = trueSender.indexOf('@'); String host = trueSender.substring(index + 1); return Domain.normalizeHostname(host, pontuacao); } } public String getSenderDomain(boolean pontuacao) { String trueSender = getSender(); if (trueSender == null) { return null; } else { int index = trueSender.indexOf('@'); try { String host = trueSender.substring(index + 1); return Domain.extractDomain(host, pontuacao); } catch (ProcessException ex) { if (pontuacao) { return '.' + trueSender.substring(index); } else { return trueSender.substring(index); } } } } public String getMailFrom() { return sender; } public String getSender() { if (sender == null) { return from == null ? replyto : from; } else if (NoReply.contains(sender, true)) { if (Domain.isValidEmail(from) && !NoReply.contains(from, true)) { return from; } else if (Domain.isValidEmail(replyto) && !NoReply.contains(replyto, true)) { return replyto; } else { return sender; } } else if (Provider.containsMX(sender) && Domain.isValidEmail(sender)) { return sender; } else if (Provider.containsDomain(getValidHostname()) && Provider.containsDomain(sender)) { if (from == null && replyto == null) { return sender; } else if (replyto == null) { return from; } else if (sender.equals(from)) { return replyto; } else { return from; } } else if (Domain.isValidEmail(sender)) { return sender; } else if (Domain.isValidEmail(from)) { return from; } else if (Domain.isValidEmail(replyto)) { return replyto; } else if (Domain.isEmail(sender)) { // Temporrio. return sender; } else if (Domain.isEmail(from)) { return from; } else if (Domain.isEmail(replyto)) { return replyto; } else { return null; } } public String getSenderSimplified(boolean byDomain, boolean pontuacao) { String trueSender = getSender(); if (trueSender == null) { return null; } else if (trueSender.startsWith("mailer-daemon@")) { return trueSender; } else if (Provider.containsMX(trueSender)) { if (Domain.isValidEmail(trueSender)) { return trueSender; } else { int index = trueSender.indexOf('@'); return trueSender.substring(index); } } else if (byDomain) { int index = trueSender.indexOf('@'); try { String host = trueSender.substring(index + 1); return Domain.extractDomain(host, pontuacao); } catch (ProcessException ex) { return trueSender.substring(index); } } else { int index = trueSender.indexOf('@'); return trueSender.substring(index); } } public String getQualifierName() { if (qualifier == null) { return "NONE"; } else { String trueSender = getSender(); if (trueSender == null) { return "NONE"; } else if (trueSender.equals(sender)) { return qualifier.name(); } else { return "NONE"; } } } public String getValidHostname() { if (hostname != null) { return hostname.length() == 0 ? null : hostname; } else if (SPF.matchHELO(ip, helo)) { return hostname = helo; } else { String host = Reverse.getValidHostname(ip); if (host == null) { hostname = ""; return null; } else if (Generic.containsGenericDomain(host)) { hostname = ""; return null; } else { return hostname = Domain.normalizeHostname(host, false); } } } public String getValidHostDomain() { try { String host = getValidHostname(); return Domain.extractDomain(host, false); } catch (ProcessException ex) { return null; } } public String getValidator(boolean authentic) { if (getSender() == null) { return null; } else if (authentic && getQualifierName().equals("PASS")) { return "PASS"; } else { String domain = getValidHostDomain(); if (domain == null) { return ip; } else { return domain; } } } public User getUser() { return User.this; } public String getUserEmail() { return User.this.getEmail(); } public String getRecipient() { return recipient; } public String getResult() { return result; } public boolean isResult(String result) { return this.result.equals(result); } public boolean isMessage(String MessageID) { if (MessageID == null || MessageID.length() == 0) { return false; } else { return MessageID.equals(this.messageID); } } private String getComplainKey() { String key = getSenderSimplified(true, true); if (key == null) { key = getOriginDomain(true); if (key == null) { key = getOrigin(true); } } return key; } public void processComplainForWhite() { String complainKey = getComplainKey(); for (long time : getTimeSet().descendingSet()) { Query query = getQuerySafe(time); if (query != null && complainKey.equals(query.getComplainKey())) { if (query.isWhite()) { query.clearBlock(); if (!query.hasMalware()) { SPF.setHam(time, query.getTokenSet()); } } } } } public void processComplainForBlock() { String complainKey = getComplainKey(); for (long time : getTimeSet().descendingSet()) { Query query = getQuerySafe(time); if (query != null && complainKey.equals(query.getComplainKey())) { if (query.isBlock()) { query.clearWhite(); complain(time); } } } } public void clearWhite() { try { White.clear(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient); } catch (ProcessException ex) { Server.logError(ex); } try { White.clear(null, User.this, ip, getSender(), getValidHostname(), getQualifierName(), recipient); } catch (ProcessException ex) { Server.logError(ex); } } public void clearBlock() { try { Block.clear(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient); } catch (ProcessException ex) { Server.logError(ex); } try { Block.clear(null, User.this, ip, getSender(), getValidHostname(), getQualifierName(), recipient); } catch (ProcessException ex) { Server.logError(ex); } if (linkMap != null) { for (String link : linkMap.keySet()) { Block.clearHREF(User.this, link, email); } } } public String getBlockSender() { Situation situation; String senderLocal = getSender(); if (senderLocal == null) { return null; } else if (senderLocal.equals(sender)) { situation = getSituation(true); } else { situation = Situation.NONE; } switch (situation) { case AUTHENTIC: case NONE: return User.this.getEmail() + ':' + getSenderSimplified(true, true); case ZONE: case IP: return User.this.getEmail() + ':' + getSenderDomain(true) + ";NOTPASS"; case SAME: String validator = getValidator(false); if (validator == null) { return null; } else { return User.this.getEmail() + ':' + getSenderSimplified(true, true) + ";" + validator; } case DOMAIN: String senderSimplified = getSenderSimplified(true, true); if (senderSimplified == null) { return null; } else { return User.this.getEmail() + ':' + senderSimplified; } case ORIGIN: case ALL: String domain = this.getOriginDomain(false); if (domain == null) { return null; } else { return User.this.getEmail() + ':' + "@;" + domain; } default: return null; } } public boolean blockSender(long time) { if (Core.isAbuseEmail(recipient)) { return Block.addSafe(User.this, getSenderSimplified(true, true) + ">" + recipient); } else { Situation situation; String senderLocal = getSender(); if (senderLocal == null) { return false; } else if (senderLocal.equals(sender)) { situation = getSituation(true); } else { situation = Situation.NONE; } return block(time, situation); } } public boolean block(long time, String situationName) { try { Situation situation = Situation.valueOf(situationName); return block(time, situation); } catch (Exception ex) { Server.logError(ex); return false; } } public boolean complain(long time) { return SPF.setSpam(time, tokenSet); } public boolean block(long time, Situation situation) { try { clearWhite(); complain(time); switch (situation) { case AUTHENTIC: case NONE: return Block.add(User.this, getSenderSimplified(true, true)); case ZONE: case IP: return Block.add(User.this, getSenderDomain(true) + ";NOTPASS"); case SAME: String validator = getValidator(false); if (validator == null) { return false; } else { return Block.add(User.this, getSenderSimplified(true, true) + ";" + validator); } case DOMAIN: String senderSimplified = getSenderSimplified(true, true); if (senderSimplified == null) { return false; } else { if (SPF.isRed(senderSimplified)) { if (Block.addExact(senderSimplified)) { Server.logDebug("new BLOCK '" + senderSimplified + "' added by '" + email + "'."); Peer.sendBlockToAll(senderSimplified); } } return Block.add(User.this, senderSimplified); } case ORIGIN: case ALL: String domain = this.getOriginDomain(false); if (domain == null) { return false; } else { return Block.add(User.this, "@;" + domain); } case RECIPIENT: String recipientAddr = getRecipient(); if (recipientAddr == null) { return false; } else { return Trap.addInexistent(User.this, recipientAddr); } default: return false; } } catch (Exception ex) { Server.logError(ex); return false; } } public boolean white(long time, String situationName) { try { Situation situation = Situation.valueOf(situationName); return white(time, situation); } catch (Exception ex) { Server.logError(ex); return false; } } public boolean whiteSender(long time) { Situation situation; String senderLocal = getSender(); if (senderLocal == null) { return false; } else if (senderLocal.equals(sender)) { situation = getSituation(true); } else { situation = getSituation(false); } return white(time, situation); } public boolean white(long time, Situation situation) { try { if (situation == null) { return false; } else { String domain; clearBlock(); SPF.setHam(time, tokenSet); switch (situation) { case ORIGIN: return White.add(User.this, "@;" + getOrigin(false)); case IP: return White.add(User.this, getSenderSimplified(false, true) + ";" + getIP()); case ZONE: domain = getValidHostDomain(); if (domain == null) { return false; } else { return White.add(User.this, getSenderSimplified(false, true) + ";" + domain); } case AUTHENTIC: return White.add(User.this, getSenderSimplified(false, true) + ";PASS"); case SAME: String validator = getValidator(false); if (validator == null) { return false; } else { return White.add(User.this, getSenderSimplified(false, true) + ";" + validator); } case RECIPIENT: String recipientAddr = getRecipient(); if (recipientAddr == null) { return false; } else { return Trap.clear(getClientEmailSet(), User.this, recipientAddr); } default: return false; } } } catch (Exception ex) { Server.logError(ex); return false; } } public boolean hasSender() { return sender != null; } public boolean hasRecipient() { return recipient != null; } public boolean hasMessageID() { return messageID != null; } public boolean hasSubject() { return subject != null; } public boolean hasMalware() { return malware != null; } public boolean isHolding() { return result.equals("HOLD"); } public boolean isHoldingFull() { if (!isHolding()) { return false; } else if (isWhiteSender()) { return false; } else if (isBlockSender()) { return false; } else { return true; } } public boolean isSuspectFull() { if (!isResult("ACCEPT")) { return false; } else if (!hasMessageID()) { return false; } else if (!hasSubject()) { return false; } else if (isWhiteSender()) { return false; } else if (isBlockSender()) { return false; } else if (hasTokenRed()) { return true; } else if (isAnyLinkRED()) { return true; } else if (hasClusterRed()) { return true; } else { return false; } } public boolean isAdminAdvised() { return adminAdvised; } public boolean isSenderAdvised() { return senderAdvised; } public boolean isRecipientAdvised() { return recipientAdvised; } public boolean isNotAdvised() { return !senderAdvised && !recipientAdvised && !adminAdvised; } public boolean isNotAdvisedLocal() { return !recipientAdvised && !adminAdvised; } public boolean isWhite() { if (sender != null && White.find(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient) != null) { return true; } else if (from != null && White.find(null, User.this, ip, from, getValidHostname(), "NONE", recipient) != null) { return true; } else if (replyto != null && White.find(null, User.this, ip, replyto, getValidHostname(), "NONE", recipient) != null) { return true; } else { return White.find(null, User.this, ip, null, getValidHostname(), "NONE", recipient) != null; } } public boolean isWhiteSender() { return White.find(null, User.this, ip, getSender(), getValidHostname(), getQualifierName(), recipient) != null; } public String getWhite() { String white; if (sender != null && (white = White.find(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient)) != null) { return white; } else if (from != null && (white = White.find(null, User.this, ip, from, getValidHostname(), "NONE", recipient)) != null) { return white; } else if (replyto != null && (white = White.find(null, User.this, ip, replyto, getValidHostname(), "NONE", recipient)) != null) { return white; } else { return White.find(null, User.this, ip, null, getValidHostname(), "NONE", recipient); } } public boolean isBlock() { if (sender != null && Block.find(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient, false, true, true, true) != null) { return true; } else if (from != null && Block.find(null, User.this, ip, from, getValidHostname(), "NONE", recipient, false, true, true, true) != null) { return true; } else if (replyto != null && Block.find(null, User.this, ip, replyto, getValidHostname(), "NONE", recipient, false, true, true, true) != null) { return true; } else { return Block.find(null, User.this, ip, null, getValidHostname(), "NONE", recipient, false, true, true, true) != null; } } public boolean isBlockSender() { if (Core.isAbuseEmail(recipient)) { return Block.containsExact(User.this, getSenderSimplified(true, true) + ">" + recipient); } else { return Block.find(null, User.this, ip, getSender(), getValidHostname(), getQualifierName(), recipient, false, false, false, false) != null; } } public boolean isAnyLinkBLOCK() { boolean blocked = false; for (String token : getLinkKeySet()) { if (Block.findHREF(User.this, token) != null) { setLinkBlocked(token); blocked = true; } } return blocked; } public boolean isAnyLinkRED() { for (String token : getLinkKeySet()) { if (SPF.isRed(token)) { return true; } else if (Block.find(User.this, token, false, false, false) != null) { return true; // } else if (Domain.isHostname(token)) { // String listed = Reverse.getListedHost(token, "multi.uribl.com", "127.0.0.2", "127.0.0.4", "127.0.0.8"); // if (listed != null) { // Server.logDebug("host " + token + " is listed in 'multi.uribl.com;" + listed + "'."); // return true; // } // } else if (Subnet.isValidIP(token)) { // String listed = Reverse.getListedIP(token, "multi.uribl.com", "127.0.0.2", "127.0.0.4", "127.0.0.8"); // if (listed != null) { // Server.logDebug("host " + token + " is listed in 'multi.uribl.com;" + listed + "'."); // return true; // } } } return false; } public String getBlock() { String block; if (sender != null && (block = Block.find(null, User.this, ip, sender, getValidHostname(), qualifier == null ? "NONE" : qualifier.name(), recipient, false, true, true, true)) != null) { return block; } else if (from != null && (block = Block.find(null, User.this, ip, from, getValidHostname(), "NONE", recipient, false, true, true, true)) != null) { return block; } else if (replyto != null && (block = Block.find(null, User.this, ip, replyto, getValidHostname(), "NONE", recipient, false, true, true, true)) != null) { return block; } else { return Block.find(null, User.this, ip, null, getValidHostname(), "NONE", recipient, false, true, true, true); } } public boolean isInexistent(Client client) { if (recipient == null) { return false; } else { return Trap.containsAnything(client, User.this, recipient); } } public boolean isRoutable() { if (recipient == null) { return true; } else { return getTrapTime() == null; } } public boolean isToPostmaster() { if (recipient == null) { return false; } else { return recipient.startsWith("postmaster@"); } } public boolean isToAdmin() { return Core.isAdminEmail(recipient); } public boolean isToAbuse() { return Core.isAbuseEmail(recipient); } public Long getTrapTime() { Long timeUser = Trap.getTime(User.this, recipient); for (String clientEmail : getClientEmailSet()) { Long timeClient = Trap.getTime(clientEmail, recipient); if (timeClient != null) { if (timeUser == null) { timeUser = timeClient; } else { timeUser = Math.min(timeClient, timeUser); } } } return timeUser; } public boolean isSenderWhite() { String validator = getValidator(true); if (validator == null) { return false; } else { return White.containsExtact(User.this, getSenderSimplified(false, true) + ';' + validator); } } public boolean isOriginWhite() { return White.containsExtact(User.this, "@;" + getOrigin(false)); } public boolean isSenderBlock(boolean validation) { if (validation) { return Block.containsExact(User.this, getSenderDomain(true) + ";NOTPASS"); } else { return Block.containsExact(User.this, getSenderSimplified(true, true)); } } public boolean isOriginBlock() { return Block.containsExact(User.this, "@;" + getOrigin(false)); } public boolean isOriginDomainBlock() { String domain = getOriginDomain(false); if (domain == null) { return false; } else { return Block.containsExact(User.this, "@;" + domain); } } public boolean isSenderRed() { String tueSender = getSender(); if (tueSender == null) { return false; } else if (getQualifierName().equals("PASS")) { String token; if (Provider.containsMX(tueSender)) { token = tueSender; } else { int index = tueSender.indexOf('@'); token = tueSender.substring(index); } Distribution distribution = SPF.getDistribution(token); if (distribution == null) { return false; } else { return distribution.isRed(); } } else { return false; } } public boolean isSenderGreen() { String tueSender = getSender(); if (tueSender == null) { return false; } else if (getQualifierName().equals("PASS")) { String token; if (Provider.containsMX(tueSender)) { token = tueSender; } else { int index = tueSender.indexOf('@'); token = tueSender.substring(index); } Distribution distribution = SPF.getDistribution(token); if (distribution == null) { return true; } else { return distribution.isGreen(); } } else { return false; } } public boolean isPass() { return qualifier == SPF.Qualifier.PASS; } public boolean isFail() { return qualifier == SPF.Qualifier.FAIL; } public boolean isSoftfail() { return qualifier == SPF.Qualifier.SOFTFAIL; } public boolean hasTokenRed() { return SPF.hasRed(tokenSet); } public boolean hasClusterRed() { return Analise.isCusterRED(ip, sender, hostname); } public boolean hasYellow() { return SPF.hasYellow(tokenSet); } public boolean isGreen() { return SPF.isGreen(tokenSet); } public boolean isSenderGood() { if (isPass()) { String mx = Domain.extractHost(sender, true); return SPF.isGood(Provider.containsExact(mx) ? sender : mx); } else { return false; } } public TreeSet<String> getLinkSet() { if (linkMap == null) { return null; } else { TreeSet<String> resultSet = new TreeSet<String>(); resultSet.addAll(linkMap.keySet()); return resultSet; } } public TreeSet<String> getTokenSet() { TreeSet<String> resultSet = new TreeSet<String>(); resultSet.addAll(tokenSet); return resultSet; } public String getMalware() { return malware; } public boolean setResult(String result) { if (result == null) { return false; } else if (result.equals("MALWARE")) { this.CHANGED.acquire(); this.malware = "FOUND"; this.result = "REJECT"; this.STORED = false; this.CHANGED.release(true); return User.CHANGED = true; } else if (!result.equals(this.result)) { this.CHANGED.acquire(); this.result = result; this.STORED = false; this.CHANGED.release(true); return User.CHANGED = true; } else { return false; } } private boolean match(String filter) { if (filter == null) { return false; } else if (filter.equals("retida") && isResult("HOLD")) { return true; } else if (filter.equals("rejeitada") && isResult("BLOCK")) { return true; } else if (filter.equals("rejeitada") && isResult("REJECT")) { return true; } else if (SubnetIPv4.isValidIPv4(filter)) { filter = SubnetIPv4.normalizeIPv4(filter); return filter.equals(getIP()); } else if (SubnetIPv6.isValidIPv6(filter)) { filter = SubnetIPv6.normalizeIPv6(filter); return filter.equals(getIP()); } else if (Domain.isValidEmail(filter)) { if (filter.equals(getMailFrom())) { return true; } else if (filter.equals(getFrom())) { return true; } else if (filter.equals(getReplyTo())) { return true; } else if (filter.equals(getRecipient())) { return true; } else { return false; } // } else if (Domain.isHostname(filter)) { // String hostname = getValidHostname(); // String mailForm = "." + Domain.extractHost(getMailFrom(), false); // String from = "." + Domain.extractHost(getFrom(), false); // String replyto = "." + Domain.extractHost(getReplyTo(), false); // String recipient = "." + Domain.extractHost(getRecipient(), false); // filter = Domain.normalizeHostname(filter, true); // if (hostname != null && hostname.endsWith(filter)) { // return true; // } else if (mailForm != null && mailForm.endsWith(filter)) { // return true; // } else if (from != null && from.endsWith(filter)) { // return true; // } else if (replyto != null && replyto.endsWith(filter)) { // return true; // } else if (recipient != null && recipient.endsWith(filter)) { // return true; // } else { // return false; // } // } else if (filter.startsWith("@") && Domain.isHostname(filter.substring(1))) { // String mailForm = Domain.extractHost(getMailFrom(), true); // String from = Domain.extractHost(getFrom(), true); // String replyto = Domain.extractHost(getReplyTo(), true); // String recipient = Domain.extractHost(getRecipient(), true); // filter = Domain.normalizeHostname(filter, true); // if (mailForm != null && mailForm.endsWith(filter)) { // return true; // } else if (from != null && from.endsWith(filter)) { // return true; // } else if (replyto != null && replyto.endsWith(filter)) { // return true; // } else if (recipient != null && recipient.endsWith(filter)) { // return true; // } else { // return false; // } } else { return false; } } public String getFrom() { return from; } public String getReplyTo() { return replyto; } public String getSubject() { return subject; } public String getMessageID() { return messageID; } public URL getUnsubscribeURL() { return unsubscribe; } public String getUnsubscribeString() { if (unsubscribe == null) { return null; } else { return unsubscribe.toExternalForm(); } } private void setLinkBlocked(String link) { if (link != null) { this.CHANGED.acquire(); if (linkMap == null) { linkMap = new TreeMap<String, Boolean>(); } this.linkMap.put(link, true); this.STORED = false; this.CHANGED.release(true); User.CHANGED = true; } } public boolean hasLinkBlocked() { for (String link : getLinkKeySet()) { if (isLinkBlocked(link)) { return true; } } return false; } public boolean isLinkBlocked(String link) { if (link == null) { return false; } else if (linkMap == null) { return false; } else { Boolean blocked = linkMap.get(link); if (blocked == null) { return false; } else { return blocked; } } } public boolean hasMiscellaneousSymbols() { return Core.hasMiscellaneousSymbols(subject); } private TreeSet<String> getLinkKeySet() { TreeSet<String> keySet = new TreeSet<String>(); if (linkMap != null) { keySet.addAll(linkMap.keySet()); } return keySet; } public boolean addLink(String link) { if (link == null) { return false; } else { this.CHANGED.acquire(); if (this.linkMap == null) { this.linkMap = new TreeMap<String, Boolean>(); } boolean blocked = false; if (isToPostmaster()) { this.linkMap.put(link, false); } else if (isToAdmin()) { this.linkMap.put(link, false); } else if (isToAbuse()) { this.linkMap.put(link, false); } else if (Block.findHREF(User.this, link) == null) { this.linkMap.put(link, false); } else { this.linkMap.put(link, true); blocked = true; } this.STORED = false; this.CHANGED.release(true); User.CHANGED = true; return blocked; } } public boolean setLinkSet(TreeSet<String> linkSet) { if (linkSet == null) { return false; } else { this.CHANGED.acquire(); if (this.linkMap == null) { this.linkMap = new TreeMap<String, Boolean>(); } boolean blocked = false; for (String link : linkSet) { if (isToPostmaster()) { this.linkMap.put(link, false); } else if (isToAdmin()) { this.linkMap.put(link, false); } else if (isToAbuse()) { this.linkMap.put(link, false); } else if (Block.findHREF(User.this, link) == null) { this.linkMap.put(link, false); } else { this.linkMap.put(link, true); blocked = true; } } this.STORED = false; this.CHANGED.release(true); User.CHANGED = true; return blocked; } } public boolean setMalware(String malware) { if (malware == null) { return false; } else if ((malware = malware.length() == 0 ? "FOUND" : malware).equals(this.malware)) { return false; } else { this.CHANGED.acquire(); this.malware = malware; this.result = "REJECT"; this.STORED = false; this.CHANGED.release(true); return User.CHANGED = true; } } public boolean needHeader() { if (!User.this.usingHeader) { return false; } else if (Generic.containsGenericSoft(sender)) { return false; } else if (Provider.containsMX(sender) && Domain.isValidEmail(sender)) { return false; } else { return Provider.containsDomain(getValidHostname()) && Provider.containsDomain(sender); } } private synchronized boolean waitHeader() { if (isResult("BLOCK")) { return false; } else if (isResult("FAIL")) { return false; } else if (isResult("GREYLIST")) { return false; } else if (isResult("INEXISTENT")) { return false; } else if (isResult("INVALID")) { return false; } else if (isResult("REJECT")) { return false; } else if (isResult("NXDOMAIN")) { return false; } else if (hasSubject()) { return true; } else if (hasMessageID()) { return true; } else if (isUsingHeader()) { try { wait(3000); return hasSubject() || hasMessageID(); } catch (InterruptedException ex) { Server.logError(ex); return false; } } else { return false; } } public synchronized String setHeader(long time, String from, String replyto, String subject, String messageID, String unsubscribe) { this.CHANGED.acquire(); User.this.usingHeader = true; if (from == null || from.length() == 0) { if (this.from != null) { this.from = null; User.CHANGED = true; } } else if (Domain.isEmail(from = from.toLowerCase()) && !from.equals(this.from)) { this.from = from; User.CHANGED = true; } if (replyto == null || replyto.length() == 0) { if (this.replyto != null) { this.replyto = null; User.CHANGED = true; } } else if (Domain.isEmail(replyto = replyto.toLowerCase()) && !replyto.equals(this.replyto)) { this.replyto = replyto; User.CHANGED = true; } if (subject == null || subject.length() == 0) { if (this.subject != null) { this.subject = null; User.CHANGED = true; } } else { try { subject = MimeUtility.decodeText(subject); subject = subject.trim(); } catch (UnsupportedEncodingException ex) { } while (subject.contains(" ")) { subject = subject.replace(" ", " "); } if (subject.length() == 0) { if (this.subject != null) { this.subject = null; User.CHANGED = true; } } else if (!subject.equals(this.subject)) { this.subject = subject; User.CHANGED = true; } } if (messageID == null || messageID.length() == 0) { if (this.messageID != null) { this.messageID = null; User.CHANGED = true; } } else { int index = messageID.indexOf('<'); if (index >= 0) { messageID = messageID.substring(index + 1); index = messageID.indexOf('>'); if (index > 0) { messageID = messageID.substring(0, index); if (!messageID.equals(this.messageID)) { this.messageID = messageID; User.CHANGED = true; } } } } boolean reject = false; if (unsubscribe != null && unsubscribe.length() > 0) { try { int index = unsubscribe.indexOf('<'); if (index >= 0) { unsubscribe = unsubscribe.substring(index + 1); index = unsubscribe.indexOf('>'); if (index > 0) { unsubscribe = unsubscribe.substring(0, index); URL url = new URL(unsubscribe); reject = addLink(url.getHost()); if (!url.equals(this.unsubscribe)) { this.unsubscribe = url; User.CHANGED = true; } } } } catch (MalformedURLException ex) { Server.logTrace("malformed unsubscribe URL: " + unsubscribe); } catch (Exception ex) { Server.logError(ex); } } String resultReturn; if (isWhite()) { this.whiteSender(time); this.result = "WHITE"; resultReturn = "WHITE"; } else if (isToPostmaster()) { resultReturn = null; } else if (isToAbuse()) { resultReturn = null; } else if (isBlock()) { this.complain(time); this.result = "BLOCK"; resultReturn = "BLOCK"; } else if (isToAdmin()) { resultReturn = null; } else if (reject) { this.complain(time); this.result = "REJECT"; resultReturn = "REJECT"; } else if (hasMiscellaneousSymbols()) { this.complain(time); this.result = "REJECT"; resultReturn = "REJECT"; } else { resultReturn = null; } this.STORED = false; this.CHANGED.release(true); this.notify(); return resultReturn; } public boolean isSpam(long time) { for (String token : tokenSet) { Distribution distribution = SPF.getDistribution(token); if (distribution != null && distribution.isSpam(time)) { return true; } } return false; } public Situation getSituation(boolean authentic) { String validator = getValidator(true); if (validator == null) { return Situation.ORIGIN; } else if (authentic && validator.equals("PASS")) { return Situation.AUTHENTIC; } else if (Subnet.isValidIP(validator)) { return Situation.IP; } else { return Situation.ZONE; } } public Situation getOriginWhiteSituation() { if (isOriginWhite()) { return Situation.ORIGIN; } else if (isWhite()) { return Situation.SAME; } else { return Situation.NONE; } } public Situation getSenderWhiteSituation() { if (isSenderWhite()) { String validator = getValidator(true); if (validator == null) { return Situation.ORIGIN; } else if (validator.equals("PASS")) { return Situation.AUTHENTIC; } else if (Subnet.isValidIP(validator)) { return Situation.IP; } else { return Situation.ZONE; } } else if (isOriginWhite()) { return Situation.ORIGIN; } else if (isWhite()) { return Situation.SAME; } else { return Situation.NONE; } } public Situation getOriginBlockSituation() { if (isOriginDomainBlock()) { return Situation.ALL; } else if (isOriginBlock()) { return Situation.ORIGIN; } else if (isBlock()) { return Situation.SAME; } else { return Situation.NONE; } } public Situation getSenderBlockSituation() { String validator = getValidator(false); if (validator == null && isOriginDomainBlock()) { return Situation.ALL; } else if (isSenderBlock(false)) { return Situation.DOMAIN; } else if (isSenderBlock(true)) { if (validator == null) { return Situation.ORIGIN; } else if (Subnet.isValidIP(validator)) { return Situation.IP; } else { return Situation.ZONE; } } else if (isOriginBlock()) { return Situation.ORIGIN; } else if (isBlock()) { return Situation.SAME; } else { return Situation.NONE; } } public synchronized boolean adviseRecipientHOLD(long time) { String mailFrom = getMailFrom(); String recipientLocal = getRecipient(); if (recipientAdvised) { return false; } else if (mailFrom == null) { return false; } else if (recipientLocal == null) { return false; } else if (!Core.hasOutputSMTP()) { return false; } else if (!Domain.isValidEmail(mailFrom)) { return false; } else if (!Domain.isValidEmail(recipientLocal)) { return false; } else if (NoReply.contains(recipientLocal, true)) { return false; } else { try { String url = Core.getUnholdURL(User.this, time); if (url == null) { return false; } else { Server.logDebug("sending retention release by e-mail."); String subjectLocal = getSubject(); if (subjectLocal == null) { if (recipientLocal.endsWith(".br") || recipientLocal.endsWith(".pt")) { subjectLocal = "Aviso de reteno de mensagem"; } else { subjectLocal = "Message retention warning"; } } String qualifierLocal = getQualifierName(); InternetAddress[] recipients = InternetAddress.parse(recipientLocal); Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props); MimeMessage message = new MimeMessage(session); message.setHeader("Date", Core.getEmailDate()); message.setFrom(Core.getAdminInternetAddress()); message.addRecipients(Message.RecipientType.TO, recipients); message.setReplyTo(User.this.getInternetAddresses()); message.setSubject(subjectLocal); Locale locale = User.this.getLocale(); // Corpo da mensagem. StringBuilder builder = new StringBuilder(); builder.append("<!DOCTYPE html>\n"); builder.append("<html lang=\""); builder.append(locale.getLanguage()); builder.append("\">\n"); builder.append(" <head>\n"); builder.append(" <meta charset=\"UTF-8\">\n"); builder.append(" <title>"); builder.append(subject); builder.append("</title>\n"); ServerHTTP.loadStyleCSS(builder); builder.append(" </head>\n"); builder.append(" <body>\n"); builder.append(" <div id=\"container\">\n"); builder.append(" <div id=\"divlogo\">\n"); builder.append(" <img src=\"cid:logo\">\n"); builder.append(" </div>\n"); if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildMessage(builder, "Aviso de reteno de mensagem"); ServerHTTP.buildText(builder, "Uma mensagem enviada por " + mailFrom + " foi retida por suspeita de SPAM."); if (!qualifierLocal.equals("PASS")) { ServerHTTP.buildText(builder, "<b>Ateno! Este remetente no pde ser autenticado. Isso significa que no h garantia desta mensagem ser genuna!</b>"); String hostDomain = getValidHostDomain(); if (hostDomain == null) { ServerHTTP.buildText(builder, "<b>No possvel determinar com segurana qual servidor disparou esta mensagem.</b>"); } else { ServerHTTP.buildText(builder, "A mensagem foi disparada por um servidor no domnio " + hostDomain + "."); } } ServerHTTP.buildText(builder, "Se voc considera esta mensagem legtima, acesse esta URL para efetivar a sua liberao:"); } else { ServerHTTP.buildMessage(builder, "Message retention warning"); ServerHTTP.buildText(builder, "A message sent from " + mailFrom + " was retained under suspicion of SPAM."); if (!qualifierLocal.equals("PASS")) { ServerHTTP.buildText(builder, "<b>Attention! This sender could not be authenticated. This means that there is no guarantee that this message will be genuine!"); String hostDomain = getValidHostDomain(); if (hostDomain == null) { ServerHTTP.buildText(builder, "<b>It is not possible to determine with certainty which server fired this message.</b>"); } else { ServerHTTP.buildText(builder, "The message was fired by a server in domain " + hostDomain + "."); } } ServerHTTP.buildText(builder, "If you consider this message legitimate, access this URL to complete its release:"); } ServerHTTP.buildText(builder, "<a href=\"" + url + "\">" + url + "</a>"); if (!User.this.isEmail(recipientLocal)) { if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildText(builder, "Para maiores informaes, entre em contato com o seu setor de TI."); } else { ServerHTTP.buildText(builder, "For more information, contact your post administrator."); } } ServerHTTP.buildFooter(builder, locale); builder.append(" </div>\n"); builder.append(" </body>\n"); builder.append("</html>\n"); // Making HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(builder.toString(), "text/html;charset=UTF-8"); // Making logo part. MimeBodyPart logoPart = new MimeBodyPart(); File logoFile = ServerHTTP.getWebFile("logo.png"); logoPart.attachFile(logoFile); logoPart.setContentID("<logo>"); logoPart.addHeader("Content-Type", "image/png"); logoPart.setDisposition(MimeBodyPart.INLINE); // Join both parts. MimeMultipart content = new MimeMultipart("related"); content.addBodyPart(htmlPart); content.addBodyPart(logoPart); // Set multiplart content. message.setContent(content); message.saveChanges(); // Enviar mensagem. return recipientAdvised = Core.sendMessage(message, 30000); } } catch (MailConnectException ex) { return false; } catch (SendFailedException ex) { if (ex.getCause() instanceof SMTPAddressFailedException) { if (ex.getCause().getMessage().contains(" 5.1.1 ")) { Trap.addInexistentSafe(User.this, recipientLocal); } } return false; } catch (Exception ex) { Server.logError(ex); return false; } } } public synchronized boolean adviseRecipientSPAM(long time) { String mailFrom = getMailFrom(); String recipientLocal = getRecipient(); String subjectLocal = getSubject(); if (recipientAdvised) { return false; } else if (mailFrom == null) { return false; } else if (subjectLocal == null) { return false; } else if (!Domain.isValidEmail(recipientLocal)) { return false; } else if (NoReply.contains(recipientLocal, true)) { return false; } else if (!Core.hasOutputSMTP()) { return false; } else { try { String url = Core.getBlockURL(User.this, time); if (url == null) { return false; } else { Server.logDebug("sending suspect alert by e-mail."); String messageidLocal = getMessageID(); InternetAddress[] recipients = InternetAddress.parse(recipientLocal); Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props); MimeMessage message = new MimeMessage(session); message.setHeader("Date", Core.getEmailDate()); message.setFrom(Core.getAdminInternetAddress()); message.addRecipients(Message.RecipientType.TO, recipients); message.setReplyTo(User.this.getInternetAddresses()); message.setSubject(subjectLocal); if (messageidLocal != null) { message.addHeader("In-Reply-To", '<' + messageidLocal + '>'); } Locale locale = User.this.getLocale(); // Corpo da mensagem. StringBuilder builder = new StringBuilder(); builder.append("<!DOCTYPE html>\n"); builder.append("<html lang=\""); builder.append(locale.getLanguage()); builder.append("\">\n"); builder.append(" <head>\n"); builder.append(" <meta charset=\"UTF-8\">\n"); builder.append(" <title>"); builder.append(subject); builder.append("</title>\n"); ServerHTTP.loadStyleCSS(builder); builder.append(" </head>\n"); builder.append(" <body>\n"); builder.append(" <body>\n"); builder.append(" <div id=\"container\">\n"); builder.append(" <div id=\"divlogo\">\n"); builder.append(" <img src=\"cid:logo\">\n"); builder.append(" </div>\n"); if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildMessage(builder, "Alerta de suspeita de SPAM"); ServerHTTP.buildText(builder, "Esta mensagem, cujo assunto foi preservado e havia sido enviada por " + mailFrom + ", foi entregue em sua caixa postal por haver nenhuma suspeita sobre ela."); ServerHTTP.buildText(builder, "Informaes mais recentes levantam forte suspeita de que esta mensagem seria SPAM."); ServerHTTP.buildText(builder, "Se voc concorda com esta nova interpretao, acesse esta URL para bloquear o remetente e para contribuir para o combate de SPAM na Internet:"); } else { ServerHTTP.buildMessage(builder, "SPAM suspected alert"); ServerHTTP.buildText(builder, "This message, whose subject was preserved and sent by " + mailFrom + ", was delivered to your mailbox because there was no suspicion about it."); ServerHTTP.buildText(builder, "More recent information raises strong suspicion that this message would be SPAM."); ServerHTTP.buildText(builder, "If you agree with this new interpretation, access this URL to block the sender and contribute to the fight against spam on the Internet:"); } ServerHTTP.buildText(builder, "<a href=\"" + url + "\">" + url + "</a>"); if (!User.this.isEmail(recipientLocal)) { String abuseEmail = Core.getAbuseEmail(); if (abuseEmail != null) { if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildText(builder, "Se voc receber qualquer mensagem de SPAM, poder encaminhar a mensagem de SPAM para " + abuseEmail + "."); ServerHTTP.buildText(builder, "Este remetente poder ser bloqueado automaticamente no caso de recebermos muitas denuncias contra ele."); } else { ServerHTTP.buildText(builder, "If you receive any SPAM message, you can forward the SPAM message to " + abuseEmail + "."); ServerHTTP.buildText(builder, "This sender may be automatically blocked if we receive too many complaints against him."); } } if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildText(builder, "Para maiores informaes, entre em contato com o seu setor de TI."); } else { ServerHTTP.buildText(builder, "For more information, contact your post administrator."); } } ServerHTTP.buildFooter(builder, locale); builder.append(" </div>\n"); builder.append(" </body>\n"); builder.append("</html>\n"); // Making HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(builder.toString(), "text/html;charset=UTF-8"); // Making logo part. MimeBodyPart logoPart = new MimeBodyPart(); File logoFile = ServerHTTP.getWebFile("logo.png"); logoPart.attachFile(logoFile); logoPart.setContentID("<logo>"); logoPart.addHeader("Content-Type", "image/png"); logoPart.setDisposition(MimeBodyPart.INLINE); // Join both parts. MimeMultipart content = new MimeMultipart("related"); content.addBodyPart(htmlPart); content.addBodyPart(logoPart); // Set multiplart content. message.setContent(content); message.saveChanges(); // Enviar mensagem. return recipientAdvised = Core.sendMessage(message, 30000); } } catch (MailConnectException ex) { return false; } catch (SendFailedException ex) { if (ex.getCause() instanceof SMTPAddressFailedException) { if (ex.getCause().getMessage().contains(" 5.1.1 ")) { Trap.addInexistentSafe(User.this, recipientLocal); } } return false; } catch (Exception ex) { Server.logError(ex); return false; } } } private synchronized boolean adviseSenderHOLD(long time) { String mailFrom = getMailFrom(); String recipientLocal = getRecipient(); if (senderAdvised) { return false; } else if (mailFrom == null) { return false; } else if (recipientLocal == null) { return false; } else if (!Core.hasOutputSMTP()) { return false; } else if (!isPass()) { return false; } else if (!Domain.isValidEmail(mailFrom)) { return false; } else if (NoReply.contains(mailFrom, true)) { return false; } else if (Generic.containsGeneric(mailFrom)) { return false; } else { try { String url = Core.getHoldingURL(User.this, time); if (url == null) { return false; } else { Server.logDebug("sending retention warning by e-mail."); Locale locale = User.this.getLocale(); String subjectLocal = getSubject(); if (subjectLocal == null) { if (locale.getLanguage().toLowerCase().equals("pt")) { subjectLocal = "Aviso de reteno de mensagem"; } else { subjectLocal = "Message retention warning"; } } String messageidLocal = getMessageID(); InternetAddress[] recipients = InternetAddress.parse(mailFrom); Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props); MimeMessage message = new MimeMessage(session); message.setHeader("Date", Core.getEmailDate()); message.setFrom(Core.getAdminInternetAddress()); message.addRecipients(Message.RecipientType.TO, recipients); message.setReplyTo(User.this.getInternetAddresses()); message.setSubject(subjectLocal); if (messageidLocal != null) { message.addHeader("In-Reply-To", '<' + messageidLocal + '>'); } // Corpo da mensagem. StringBuilder builder = new StringBuilder(); builder.append("<!DOCTYPE html>\n"); builder.append("<html lang=\""); builder.append(locale.getLanguage()); builder.append("\">\n"); builder.append(" <head>\n"); builder.append(" <meta charset=\"UTF-8\">\n"); builder.append(" <title>"); builder.append(subject); builder.append("</title>\n"); ServerHTTP.loadStyleCSS(builder); builder.append(" </head>\n"); builder.append(" <body>\n"); builder.append(" <div id=\"container\">\n"); builder.append(" <div id=\"divlogo\">\n"); builder.append(" <img src=\"cid:logo\">\n"); builder.append(" </div>\n"); if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildMessage(builder, "Aviso de reteno de mensagem"); ServerHTTP.buildText(builder, "Esta mensagem, que foi enviada para " + recipientLocal + " foi retida por suspeita de SPAM."); ServerHTTP.buildText(builder, "Se voc considera isto um engano, acesse esta URL para solicitar a sua liberao:"); } else { ServerHTTP.buildMessage(builder, "Message retention warning"); ServerHTTP.buildText(builder, "This message, which was sent to " + recipientLocal + " was retained under suspicion of SPAM."); ServerHTTP.buildText(builder, "If you consider this a mistake, access this URL to request its release:"); } ServerHTTP.buildText(builder, "<a href=\"" + url + "\">" + url + "</a>"); ServerHTTP.buildFooter(builder, locale); builder.append(" </div>\n"); builder.append(" </body>\n"); builder.append("</html>\n"); // Making HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(builder.toString(), "text/html;charset=UTF-8"); // Making logo part. MimeBodyPart logoPart = new MimeBodyPart(); File logoFile = ServerHTTP.getWebFile("logo.png"); logoPart.attachFile(logoFile); logoPart.setContentID("<logo>"); logoPart.addHeader("Content-Type", "image/png"); logoPart.setDisposition(MimeBodyPart.INLINE); // Join both parts. MimeMultipart content = new MimeMultipart("related"); content.addBodyPart(htmlPart); content.addBodyPart(logoPart); // Set multiplart content. message.setContent(content); message.saveChanges(); // Enviar mensagem. return senderAdvised = Core.sendMessage(message, 30000); } } catch (NameNotFoundException ex) { blockSender(time); return false; } catch (MailConnectException ex) { blockSender(time); return false; } catch (SendFailedException ex) { blockSender(time); return false; } catch (MessagingException ex) { blockSender(time); return false; } catch (Exception ex) { Server.logError(ex); return false; } } } private synchronized boolean adviseSenderBLOCK(long time) { String mailFrom = getMailFrom(); String recipientLocal = getRecipient(); if (senderAdvised) { return false; } else if (mailFrom == null) { return false; } else if (recipientLocal == null) { return false; } else if (!isPass()) { return false; } else if (!Core.hasOutputSMTP()) { return false; } else if (!Domain.isValidEmail(mailFrom)) { return false; } else if (NoReply.contains(mailFrom, true)) { return false; } else if (Generic.containsGeneric(mailFrom)) { return false; } else if (isSenderWhite()) { return false; } else if (isSenderBlock(false)) { return false; } else { try { String url = getUnblockURL(); if (url == null) { return false; } else { Server.logDebug("sending blocked warning by e-mail."); Locale locale = User.this.getLocale(); String subjectLocal = getSubject(); if (subjectLocal == null) { if (locale.getLanguage().toLowerCase().equals("pt")) { subjectLocal = "Aviso de rejeio de mensagem"; } else { subjectLocal = "Message retention warning"; } } String messageidLocal = getMessageID(); InternetAddress[] recipients = InternetAddress.parse(mailFrom); Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props); MimeMessage message = new MimeMessage(session); message.setHeader("Date", Core.getEmailDate()); message.setFrom(Core.getAdminInternetAddress()); message.addRecipients(Message.RecipientType.TO, recipients); // message.addRecipients(Message.RecipientType.BCC, InternetAddress.parse("leandro@spfbl.net")); // Temporrio. message.setReplyTo(User.this.getInternetAddresses()); message.setSubject(subjectLocal); if (messageidLocal != null) { message.addHeader("In-Reply-To", '<' + messageidLocal + '>'); } // Corpo da mensagem. StringBuilder builder = new StringBuilder(); builder.append("<!DOCTYPE html>\n"); builder.append("<html lang=\""); builder.append(locale.getLanguage()); builder.append("\">\n"); builder.append(" <head>\n"); builder.append(" <meta charset=\"UTF-8\">\n"); builder.append(" <title>"); builder.append(subjectLocal); builder.append("</title>\n"); ServerHTTP.loadStyleCSS(builder); builder.append(" </head>\n"); builder.append(" <body>\n"); builder.append(" <div id=\"container\">\n"); builder.append(" <div id=\"divlogo\">\n"); builder.append(" <img src=\"cid:logo\">\n"); builder.append(" </div>\n"); if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildMessage(builder, "Aviso de bloqueio de mensagem"); ServerHTTP.buildText(builder, "A mensagem, que foi enviada para " + recipientLocal + " foi rejeitada por bloqueio manual."); ServerHTTP.buildText(builder, "Se voc considera isto um engano, acesse esta URL para solicitar o desbloqueio:"); } else { ServerHTTP.buildMessage(builder, "Message block warning"); ServerHTTP.buildText(builder, "The message, which was sent to " + recipientLocal + " was rejected by manual block."); ServerHTTP.buildText(builder, "If you consider this a mistake, access this URL to request unblock:"); } ServerHTTP.buildText(builder, "<a href=\"" + url + "\">" + url + "</a>"); ServerHTTP.buildFooter(builder, locale); builder.append(" </div>\n"); builder.append(" </body>\n"); builder.append("</html>\n"); // Making HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(builder.toString(), "text/html;charset=UTF-8"); // Making logo part. MimeBodyPart logoPart = new MimeBodyPart(); File logoFile = ServerHTTP.getWebFile("logo.png"); logoPart.attachFile(logoFile); logoPart.setContentID("<logo>"); logoPart.addHeader("Content-Type", "image/png"); logoPart.setDisposition(MimeBodyPart.INLINE); // Join both parts. MimeMultipart content = new MimeMultipart("related"); content.addBodyPart(htmlPart); content.addBodyPart(logoPart); // Set multiplart content. message.setContent(content); message.saveChanges(); // Enviar mensagem. if (Core.sendMessage(message, 30000)) { this.CHANGED.acquire(); this.senderAdvised = true; User.CHANGED = true; this.CHANGED.release(true); blockSender(time); return true; } else { return false; } } } catch (NameNotFoundException ex) { blockSender(time); return false; } catch (MailConnectException ex) { blockSender(time); return false; } catch (SendFailedException ex) { blockSender(time); return false; } catch (MessagingException ex) { blockSender(time); return false; } catch (Exception ex) { Server.logError(ex); return false; } } } private synchronized boolean adviseAdminHOLD(long time) { String senderLocal = getSender(); String userEmail = getEmail(); if (adminAdvised) { return false; } else if (senderLocal == null) { return false; } else if (userEmail == null) { return false; } else if (!Domain.isValidEmail(senderLocal)) { return false; } else if (NoReply.contains(userEmail, true)) { return false; } else if (!Core.hasOutputSMTP()) { return false; } else { try { String unholdURL = Core.getUnholdURL(User.this, time); String blockURL = Core.getBlockURL(User.this, time); if (unholdURL == null) { return false; } else if (blockURL == null) { return false; } else { Server.logDebug("sending retention warning by e-mail."); Locale locale = User.this.getLocale(); String subjectLocal = getSubject(); if (subjectLocal == null) { if (locale.getLanguage().toLowerCase().equals("pt")) { subjectLocal = "Aviso de rejeio de mensagem"; } else { subjectLocal = "Message retention warning"; } } String qualifierLocal = getQualifierName(); String recipientLocal = getRecipient(); String messageidLocal = getMessageID(); TreeSet<String> linkSet = getLinkSet(); InternetAddress[] recipients = User.this.getInternetAddresses(); Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props); MimeMessage message = new MimeMessage(session); message.setHeader("Date", Core.getEmailDate()); message.setFrom(Core.getAdminInternetAddress()); message.addRecipients(Message.RecipientType.TO, recipients); message.setReplyTo(InternetAddress.parse(senderLocal)); message.setSubject(subjectLocal); if (messageidLocal != null) { message.setHeader("Message-ID", messageidLocal); } // Corpo da mensagem. StringBuilder builder = new StringBuilder(); builder.append("<!DOCTYPE html>\n"); builder.append("<html lang=\""); builder.append(locale.getLanguage()); builder.append("\">\n"); builder.append(" <head>\n"); builder.append(" <meta charset=\"UTF-8\">\n"); builder.append(" <title>"); builder.append(subject); builder.append("</title>\n"); ServerHTTP.loadStyleCSS(builder); builder.append(" </head>\n"); builder.append(" <body>\n"); builder.append(" <div id=\"container\">\n"); builder.append(" <div id=\"divlogo\">\n"); builder.append(" <img src=\"cid:logo\">\n"); builder.append(" </div>\n"); if (locale.getLanguage().toLowerCase().equals("pt")) { ServerHTTP.buildMessage(builder, "Aviso de reteno de mensagem"); ServerHTTP.buildText(builder, "Uma mensagem enviada de " + senderLocal + " para " + recipientLocal + " foi retida por suspeita de SPAM."); if (recipientAdvised) { ServerHTTP.buildText(builder, "O destinatrio j foi avisado sobre a reteno, porm ele no liberou a mensagem ainda."); } else if (senderAdvised) { ServerHTTP.buildText(builder, "O remetente j foi avisado sobre a reteno, porm ele no solicitou a liberao da mensagem ainda."); } if (!qualifierLocal.equals("PASS")) { ServerHTTP.buildText(builder, "<b>Ateno! Este remetente no pde ser autenticado. Isso significa que a mensagem pode ser uma fraude!</b>"); String hostDomain = getValidHostDomain(); if (hostDomain == null) { ServerHTTP.buildText(builder, "<b>No possvel determinar com segurana qual servidor disparou esta mensagem.</b>"); } else { ServerHTTP.buildText(builder, "A mensagem foi disparada por um servidor no domnio " + hostDomain + "."); } } if (linkSet != null && !linkSet.isEmpty()) { ServerHTTP.buildText(builder, "Os seguintes elementos foram encontrados dentro da mensagem:"); builder.append(" <ul>\n"); for (String link : linkSet) { builder.append(" <li>"); if (isLinkBlocked(link)) { builder.append("<b><font color=\"DarkRed\">"); builder.append(link); builder.append("</font></b>"); } else { builder.append(link); } builder.append("</li>\n"); } builder.append(" </ul>\n"); } ServerHTTP.buildText(builder, "Se voc considera esta mensagem legtima, acesse esta URL para solicitar a sua liberao:"); ServerHTTP.buildText(builder, "<a href=\"" + unholdURL + "\">" + unholdURL + "</a>"); ServerHTTP.buildText(builder, "Se voc considera esta mensagem SPAM, acesse esta URL para bloquear o remetente:"); ServerHTTP.buildText(builder, "<a href=\"" + blockURL + "\">" + blockURL + "</a>"); } else { ServerHTTP.buildMessage(builder, "Message retention warning"); ServerHTTP.buildText(builder, "A message sent from " + senderLocal + " to " + recipientLocal + " was retained under suspicion of SPAM."); if (recipientAdvised) { ServerHTTP.buildText(builder, "The recipient has been warned about retention, but he did not release the message yet."); } else if (senderAdvised) { ServerHTTP.buildText(builder, "The sender has already been advised of the retention, but he has not requested to release the message yet."); } if (!qualifierLocal.equals("PASS")) { ServerHTTP.buildText(builder, "<b>Attention! This sender could not be authenticated. That means the message can be a fraud!</b>"); String hostDomain = getValidHostDomain(); if (hostDomain == null) { ServerHTTP.buildText(builder, "<b>It is not possible to determine with certainty which server fired this message.</b>"); } else { ServerHTTP.buildText(builder, "<p>The message was fired by a server in domain " + hostDomain + "."); } } if (linkSet != null && !linkSet.isEmpty()) { ServerHTTP.buildText(builder, "The following elements have been found inside message:"); builder.append(" <ul>\n"); for (String link : linkSet) { builder.append(" <li>"); if (isLinkBlocked(link)) { builder.append("<b><font color=\"DarkRed\">"); builder.append(link); builder.append("</font></b>"); } else { builder.append(link); } builder.append("</li>\n"); } builder.append(" </ul>\n"); } ServerHTTP.buildText(builder, "If you consider this message legitimate, access this URL to request its release:"); ServerHTTP.buildText(builder, "<a href=\"" + unholdURL + "\">" + unholdURL + "</a>"); ServerHTTP.buildText(builder, "If you consider this SPAM message, access this URL to block the sender:"); ServerHTTP.buildText(builder, "<a href=\"" + blockURL + "\">" + blockURL + "</a>"); } ServerHTTP.buildFooter(builder, locale); builder.append(" </div>\n"); builder.append(" </body>\n"); builder.append("</html>\n"); // Making HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(builder.toString(), "text/html;charset=UTF-8"); // Making logo part. MimeBodyPart logoPart = new MimeBodyPart(); File logoFile = ServerHTTP.getWebFile("logo.png"); logoPart.attachFile(logoFile); logoPart.setContentID("<logo>"); logoPart.addHeader("Content-Type", "image/png"); logoPart.setDisposition(MimeBodyPart.INLINE); // Join both parts. MimeMultipart content = new MimeMultipart("related"); content.addBodyPart(htmlPart); content.addBodyPart(logoPart); // Set multiplart content. message.setContent(content); message.saveChanges(); // Enviar mensagem. return adminAdvised = Core.sendMessage(message, 30000); } } catch (MailConnectException ex) { return false; } catch (SendFailedException ex) { return false; } catch (Exception ex) { Server.logError(ex); return false; } } } @Override public String toString() { return client + ": " + (helo == null ? ip : helo + " [" + ip + "]") + (getSender() == null ? "" : " " + getSender()) + " " + getQualifierName() + " > " + recipient + " = " + result; } } }