Coverage Report - com.nilhcem.fakesmtp.server.MailSaver
 
Classes in this File Line Coverage Branch Coverage Complexity
MailSaver
71%
59/83
56%
18/32
5
 
 1  
 package com.nilhcem.fakesmtp.server;
 2  
 
 3  
 import com.nilhcem.fakesmtp.core.ArgsHandler;
 4  
 import com.nilhcem.fakesmtp.core.Configuration;
 5  
 import com.nilhcem.fakesmtp.core.I18n;
 6  
 import com.nilhcem.fakesmtp.model.EmailModel;
 7  
 import com.nilhcem.fakesmtp.model.UIModel;
 8  
 import org.apache.commons.io.FileUtils;
 9  
 import org.slf4j.Logger;
 10  
 import org.slf4j.LoggerFactory;
 11  
 
 12  
 import java.io.BufferedReader;
 13  
 import java.io.File;
 14  
 import java.io.InputStream;
 15  
 import java.io.InputStreamReader;
 16  
 import java.io.IOException;
 17  
 import java.io.StringReader;
 18  
 import java.nio.charset.Charset;
 19  
 import java.text.SimpleDateFormat;
 20  
 import java.util.Date;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 import java.util.Observable;
 24  
 import java.util.regex.Matcher;
 25  
 import java.util.regex.Pattern;
 26  
 
 27  
 /**
 28  
  * Saves emails and notifies components so they can refresh their views with new data.
 29  
  *
 30  
  * @author Nilhcem
 31  
  * @since 1.0
 32  
  */
 33  2
 public final class MailSaver extends Observable {
 34  
 
 35  1
         private static final Logger LOGGER = LoggerFactory.getLogger(MailSaver.class);
 36  1
         private static final String LINE_SEPARATOR = System.getProperty("line.separator");
 37  
         // This can be a static variable since it is Thread Safe
 38  1
         private static final Pattern SUBJECT_PATTERN = Pattern.compile("^Subject: (.*)$");
 39  
 
 40  2
         private final SimpleDateFormat dateFormat = new SimpleDateFormat("ddMMyyhhmmssSSS");
 41  
 
 42  
         /**
 43  
          * Saves incoming email in file system and notifies observers.
 44  
          *
 45  
          * @param from the user who send the email.
 46  
          * @param to the recipient of the email.
 47  
          * @param data an InputStream object containing the email.
 48  
          * @see com.nilhcem.fakesmtp.gui.MainPanel#addObservers to see which observers will be notified
 49  
          */
 50  
         public void saveEmailAndNotify(String from, String to, InputStream data) {
 51  1
                 List<String> relayDomains = UIModel.INSTANCE.getRelayDomains();
 52  
 
 53  1
                 if (relayDomains != null) {
 54  0
                         boolean matches = false;
 55  0
                         for (String domain : relayDomains) {
 56  0
                                 if (to.endsWith(domain)) {
 57  0
                                         matches = true;
 58  0
                                         break;
 59  
                                 }
 60  0
                         }
 61  
 
 62  0
                         if (!matches) {
 63  0
                                 LOGGER.debug("Destination {} doesn't match relay domains", to);
 64  0
                                 return;
 65  
                         }
 66  
                 }
 67  
 
 68  
                 // We move everything that we can move outside the synchronized block to limit the impact
 69  1
                 EmailModel model = new EmailModel();
 70  1
                 model.setFrom(from);
 71  1
                 model.setTo(to);
 72  1
                 String mailContent = convertStreamToString(data);
 73  1
                 model.setSubject(getSubjectFromStr(mailContent));
 74  1
                 model.setEmailStr(mailContent);
 75  
 
 76  1
                 synchronized (getLock()) {
 77  1
                         String filePath = saveEmailToFile(mailContent);
 78  
 
 79  1
                         model.setReceivedDate(new Date());
 80  1
                         model.setFilePath(filePath);
 81  
 
 82  1
                         setChanged();
 83  1
                         notifyObservers(model);
 84  1
                 }
 85  1
         }
 86  
 
 87  
         /**
 88  
          * Deletes all received emails from file system.
 89  
          */
 90  
         public void deleteEmails() {
 91  1
                 Map<Integer, String> mails = UIModel.INSTANCE.getListMailsMap();
 92  1
                 if (ArgsHandler.INSTANCE.memoryModeEnabled()) {
 93  0
                         return;
 94  
                 }
 95  1
                 for (String value : mails.values()) {
 96  1
                         File file = new File(value);
 97  1
                         if (file.exists()) {
 98  
                                 try {
 99  1
                                         if (!file.delete()) {
 100  0
                                                 LOGGER.error("Impossible to delete file {}", value);
 101  
                                         }
 102  0
                                 } catch (SecurityException e) {
 103  0
                                         LOGGER.error("", e);
 104  1
                                 }
 105  
                         }
 106  1
                 }
 107  1
         }
 108  
 
 109  
         /**
 110  
          * Returns a lock object.
 111  
          * <p>
 112  
          * This lock will be used to make the application thread-safe, and
 113  
          * avoid receiving and deleting emails in the same time.
 114  
          * </p>
 115  
          *
 116  
          * @return a lock object <i>(which is actually the current instance of the {@code MailSaver} object)</i>.
 117  
          */
 118  
         public Object getLock() {
 119  2
                 return this;
 120  
         }
 121  
 
 122  
         /**
 123  
          * Converts an {@code InputStream} into a {@code String} object.
 124  
          * <p>
 125  
          * The method will not copy the first 4 lines of the input stream.<br>
 126  
          * These 4 lines are SubEtha SMTP additional information.
 127  
          * </p>
 128  
          *
 129  
          * @param is the InputStream to be converted.
 130  
          * @return the converted string object, containing data from the InputStream passed in parameters.
 131  
          */
 132  
         private String convertStreamToString(InputStream is) {
 133  1
                 final long lineNbToStartCopy = 4; // Do not copy the first 4 lines (received part)
 134  1
                 BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName(I18n.UTF8)));
 135  1
                 StringBuilder sb = new StringBuilder();
 136  
 
 137  
                 String line;
 138  1
                 long lineNb = 0;
 139  
                 try {
 140  16
                         while ((line = reader.readLine()) != null) {
 141  15
                                 if (++lineNb > lineNbToStartCopy) {
 142  11
                                         sb.append(line).append(LINE_SEPARATOR);
 143  
                                 }
 144  
                         }
 145  0
                 } catch (IOException e) {
 146  0
                         LOGGER.error("", e);
 147  1
                 }
 148  1
                 return sb.toString();
 149  
         }
 150  
 
 151  
         /**
 152  
          * Saves the content of the email passed in parameters in a file.
 153  
          *
 154  
          * @param mailContent the content of the email to be saved.
 155  
          * @return the path of the created file.
 156  
          */
 157  
         private String saveEmailToFile(String mailContent) {
 158  1
                 if (ArgsHandler.INSTANCE.memoryModeEnabled()) {
 159  0
                         return null;
 160  
                 }
 161  2
                 String filePath = String.format("%s%s%s", UIModel.INSTANCE.getSavePath(), File.separator,
 162  1
                                 dateFormat.format(new Date()));
 163  
 
 164  
                 // Create file
 165  1
                 int i = 0;
 166  1
                 File file = null;
 167  2
                 while (file == null || file.exists()) {
 168  
                         String iStr;
 169  1
                         if (i++ > 0) {
 170  0
                                 iStr = Integer.toString(i);
 171  
                         } else {
 172  1
                                 iStr = "";
 173  
                         }
 174  1
                         file = new File(filePath + iStr + Configuration.INSTANCE.get("emails.suffix"));
 175  1
                 }
 176  
 
 177  
                 // Copy String to file
 178  
                 try {
 179  1
                         FileUtils.writeStringToFile(file, mailContent);
 180  0
                 } catch (IOException e) {
 181  
                         // If we can't save file, we display the error in the SMTP logs
 182  0
                         Logger smtpLogger = LoggerFactory.getLogger(org.subethamail.smtp.server.Session.class);
 183  0
                         smtpLogger.error("Error: Can't save email: {}", e.getMessage());
 184  1
                 }
 185  1
                 return file.getAbsolutePath();
 186  
         }
 187  
 
 188  
         /**
 189  
          * Gets the subject from the email data passed in parameters.
 190  
          *
 191  
          * @param data a string representing the email content.
 192  
          * @return the subject of the email, or an empty subject if not found.
 193  
          */
 194  
         private String getSubjectFromStr(String data) {
 195  
                 try {
 196  1
                         BufferedReader reader = new BufferedReader(new StringReader(data));
 197  
 
 198  
                         String line;
 199  5
                         while ((line = reader.readLine()) != null) {
 200  5
                                  Matcher matcher = SUBJECT_PATTERN.matcher(line);
 201  5
                                  if (matcher.matches()) {
 202  1
                                          return matcher.group(1);
 203  
                                  }
 204  4
                         }
 205  0
                 } catch (IOException e) {
 206  0
                         LOGGER.error("", e);
 207  0
                 }
 208  0
                 return "";
 209  
         }
 210  
 }