/** * ESUP-Portail Helpdesk - Copyright (c) 2004-2009 ESUP-Portail consortium. */ package org.esupportail.helpdesk.services.feed.imap; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Set; import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Part; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; import org.apache.commons.io.IOUtils; import org.apache.turbine.services.mimetype.util.MimeTypeMap; import org.esupportail.commons.services.i18n.I18nService; import org.esupportail.commons.services.logging.Logger; import org.esupportail.commons.services.logging.LoggerImpl; import org.esupportail.commons.utils.Assert; import org.esupportail.helpdesk.domain.ActionScope; import org.esupportail.helpdesk.domain.DomainService; import org.esupportail.helpdesk.domain.beans.Category; import org.esupportail.helpdesk.domain.beans.Department; import org.esupportail.helpdesk.domain.beans.FileInfo; import org.esupportail.helpdesk.domain.beans.Ticket; import org.esupportail.helpdesk.domain.beans.User; import org.esupportail.helpdesk.services.feed.ErrorHolder; import org.esupportail.helpdesk.services.feed.imap.spam.SpamFilter; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.StringUtils; import com.sun.mail.imap.IMAPBodyPart; /** * A basic TicketMessageReader implementation. */ public class TicketMessageReaderImpl implements InitializingBean, TicketMessageReader { /** * The serialization id. */ private static final long serialVersionUID = -7866074442898790724L; /** * A logger. */ private final Logger logger = new LoggerImpl(getClass()); /** * The domain service. */ private DomainService domainService; /** * The i18n service. */ private I18nService i18nService; /** * Constructor. */ public TicketMessageReaderImpl() { super(); } /** * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() { Assert.notNull(domainService, "property domainService of class " + this.getClass().getName() + " can not be null"); Assert.notNull(i18nService, "property i18nService of class " + this.getClass().getName() + " can not be null"); } /** * @return the recipients of a message. * @param message * @param errorHolder */ protected Set<Address> extractRecipients( final Message message, final ErrorHolder errorHolder) { if (logger.isDebugEnabled()) { logger.debug("extractRecipients(" + message + ")"); } Set<Address> recipients = new HashSet<Address>(); try { Address [] addresses; addresses = message.getRecipients(Message.RecipientType.TO); if (addresses != null) { for (Address address : addresses) { Address decodedAddress = address; if (address instanceof InternetAddress) { InternetAddress iAddress = (InternetAddress) address; try { decodedAddress = new InternetAddress( iAddress.getAddress(), decodeText(iAddress.getPersonal())); } catch (UnsupportedEncodingException e) { // keep as is } } errorHolder.addInfo("To: " + decodedAddress); recipients.add(decodedAddress); } } addresses = message.getRecipients(Message.RecipientType.CC); if (addresses != null) { for (Address address : addresses) { Address decodedAddress = address; if (address instanceof InternetAddress) { InternetAddress iAddress = (InternetAddress) address; try { decodedAddress = new InternetAddress( iAddress.getAddress(), decodeText(iAddress.getPersonal())); } catch (UnsupportedEncodingException e) { // keep as is } } errorHolder.addInfo("Cc: " + decodedAddress); recipients.add(decodedAddress); } } } catch (MessagingException e) { errorHolder.addError("could not get the recipients: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } } return recipients; } /** * @return the sender of a message. * @param message * @param errorHolder */ protected User extractSender( final Message message, final ErrorHolder errorHolder) { if (logger.isDebugEnabled()) { logger.debug("extractSender(" + message + ")"); } Address [] fromAddresses; try { fromAddresses = message.getFrom(); } catch (MessagingException e) { errorHolder.addError("could not get the From header: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } if (fromAddresses == null || fromAddresses.length == 0) { errorHolder.addError("no sender found."); return null; } Address senderAddress = fromAddresses[0]; if (logger.isDebugEnabled()) { logger.debug("From: " + senderAddress); } InternetAddress internetAddress; try { internetAddress = new InternetAddress(senderAddress.toString()); } catch (AddressException e) { errorHolder.addError("ill-formed sender [" + senderAddress + "]: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } String email = internetAddress.getAddress(); String personal = internetAddress.getPersonal(); User sender = getDomainService().getUserStore().getUserWithEmail(email); if (sender == null) { errorHolder.addError("could not find any user with email [" + email + "]."); return null; } if (getDomainService().getUserStore().isApplicationUser(sender) && sender.getId().equals(sender.getDisplayName()) && StringUtils.hasText(personal)) { sender.setDisplayName(personal); domainService.updateUser(sender); } errorHolder.addInfo("From: " + sender.getId() + " (" + sender.getDisplayName() + ")"); return sender; } /** * Used to decode the subject and filename. * @param input * @return the decoded text */ protected String decodeText(final String input) { if (input == null) { return null; } String decoded; try { decoded = MimeUtility.decodeText(input); } catch (UnsupportedEncodingException e) { return input; } if (input.equals(decoded)) { return input; } return decodeText(decoded); } /** * @return the subject of a message. * @param message * @param errorHolder */ protected String extractSubject( final Message message, final ErrorHolder errorHolder) { if (logger.isDebugEnabled()) { logger.debug("extractsubject(" + message + ")"); } String subject; try { subject = decodeText(message.getSubject()); } catch (MessagingException e) { errorHolder.addError("invalid sender: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } if (subject == null) { subject = "(no subject)"; } try { subject = MimeUtility.decodeText(subject); } catch (UnsupportedEncodingException e) { errorHolder.addInfo("could not decode the subject, keeping it encoded"); } errorHolder.addInfo("Subject: " + subject); return subject; } /** * @return the content-type of a message part. * @param part * @param errorHolder */ protected String extractContentType( final Part part, final ErrorHolder errorHolder) { if (logger.isDebugEnabled()) { logger.debug("extractContentType(" + part + ")"); } String contentType; try { contentType = part.getContentType(); } catch (MessagingException e) { errorHolder.addError("could not get part content-type: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } if (contentType == null) { errorHolder.addError("no content-type found."); return null; } errorHolder.addInfo("Content-Type: " + contentType); return contentType; } /** * @return the content-type of a message part. * @param message * @param errorHolder */ protected byte[] extractData( final Message message, final ErrorHolder errorHolder) { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); message.writeTo(os); byte[] data = os.toByteArray(); errorHolder.addInfo("message data read."); return data; } catch (IOException e) { errorHolder.addError("could not read mail data: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } catch (MessagingException e) { errorHolder.addError("could not get mail data: " + e.getMessage()); if (logger.isDebugEnabled()) { logger.debug(e); } return null; } } /** * Build a part index. * @param upperIndex * @param i * @return the index */ protected String getPartIndex( final String upperIndex, final int i) { return upperIndex + "-" + i; } /** * Get the content of a message part. * @param part * @param errorHolder * @return the content as an Object, or null. */ protected Object getPartContent( final Part part, final ErrorHolder errorHolder) { errorHolder.addInfo("getting the content of part " + part + "..."); Object content = null; try { try { content = part.getContent(); } catch (UnsupportedEncodingException e) { if (part.isMimeType("text/plain")) { errorHolder.addInfo( "could not get mail content because of an unknown charset: " + e.getMessage()); errorHolder.addInfo("trying to get the stream..."); content = IOUtils.toString(part.getInputStream()); errorHolder.addInfo("stream read."); } else { errorHolder.addError( "could not get mail content because of an unknown charset: " + e.getMessage()); return null; } } } catch (IOException e) { errorHolder.addError("could not get part content: " + e.getMessage()); return null; } catch (MessagingException e) { errorHolder.addError("could not get part content: " + e.getMessage()); return null; } if (content == null) { errorHolder.addError("content not found"); return null; } if (content instanceof Message) { errorHolder.addInfo("content is a message itself: " + content); content = getPartContent((Message) content, errorHolder); } errorHolder.addInfo("content of part " + part + " extracted: " + content.getClass()); return content; } /** * Get the content of a message part as a String. * @param part the message part * @param errorHolder * @return a String. */ protected String getPartContentAsString( final Part part, final ErrorHolder errorHolder) { Object content = getPartContent(part, errorHolder); if (content == null) { return null; } if (content instanceof String) { return (String) content; } errorHolder.addError( "getPartContentAsString(): unexpected class " + content.getClass().getName() + " (" + String.class.getName() + " expected)"); return null; } /** * Get the content of a message part as a multipart or String. * @param part the message part * @param errorHolder * @return a String. */ protected MimeMultipart getPartContentAsMultipart( final Part part, final ErrorHolder errorHolder) { Object content = getPartContent(part, errorHolder); if (content == null) { return null; } if (content instanceof MimeMultipart) { return (MimeMultipart) content; } errorHolder.addError( "getPartContentAsMultipart(): unexpected class " + content.getClass().getName() + " (" + MimeMultipart.class.getName() + " expected)"); return null; } /** * Get the content of a message part as a multipart or String. * @param part the message part * @param errorHolder * @return a String. */ protected Object getPartContentAsMultipartOrString( final Part part, final ErrorHolder errorHolder) { Object content = getPartContent(part, errorHolder); if (content == null) { return null; } if (content instanceof MimeMultipart) { return content; } if (content instanceof String) { return content; } errorHolder.addError( "getPartContentAsMultipartOrString(): unexpected class " + content.getClass().getName() + " (" + MimeMultipart.class.getName() + " or " + String.class.getName() + " expected)"); return null; } /** * Convert a content to an array of bytes. * @param content the content * @param errorHolder * @return an array of bytes. */ protected byte [] contentToByteArray( final Object content, final ErrorHolder errorHolder) { if (content instanceof String) { return ((String) content).getBytes(); } if (content instanceof InputStream) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int ch; while ((ch = ((InputStream) content).read()) != -1) { baos.write(ch); } return baos.toByteArray(); } catch (IOException e) { errorHolder.addError( "simpleContentToByteArray(): + " + e.getMessage()); return null; } } if (content instanceof MimeMultipart) { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); ((MimeMultipart) content).writeTo(os); return os.toByteArray(); } catch (IOException e) { errorHolder.addError( "could not convert multipart content to an array of bytes: " + e.getMessage()); return null; } catch (MessagingException e) { errorHolder.addError( "could not convert multipart content to an array of bytes: " + e.getMessage()); return null; } } errorHolder.addError( "simpleContentToByteArray(): unexpected class " + content.getClass().getName() + " (" + String.class.getName() + ", " + " (" + MimeMultipart.class.getName() + " or " + InputStream.class.getName() + " expected)"); return null; } /** * Get the filename associated with a part. * @param part the part * @param ticket * @param partIndex * @param ext the extension to use (if not null). * @param errorHolder * @return a String. */ protected String getPartFilename( final Part part, final Ticket ticket, final String partIndex, final String ext, final ErrorHolder errorHolder) { String filename = null; try { filename = decodeText(part.getFileName()); } catch (MessagingException e) { errorHolder.addInfo("could not get part filename: " + e.getMessage()); errorHolder.addInfo("building a filename..."); } if (filename == null) { String extension = ext; if (extension == null) { String contentType = extractContentType(part, errorHolder); if (contentType != null) { extension = new MimeTypeMap().getDefaultExtension(contentType.toLowerCase()); } if (extension == null) { errorHolder.addInfo("unknown content-type " + contentType + ", using bin"); extension = ".bin"; } } filename = ticket.getId() + partIndex + extension; } errorHolder.addInfo("Filename: " + filename); return filename; } /** * Get a plain/text part. * @param part * @param ticket * @param partIndex * @param errorHolder */ protected void getTextPart( final Part part, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing text/plain part " + part + "..."); String content = getPartContentAsString(part, errorHolder); if (!StringUtils.hasText(content)) { return; } byte [] contentByteArray = contentToByteArray(content, errorHolder); if (contentByteArray == null) { return; } String filename = getPartFilename(part, ticket, partIndex, ".txt", errorHolder); getDomainService().addFileInfo(new FileInfo( filename, contentByteArray, ticket, null, ActionScope.DEFAULT)); errorHolder.addInfo("added attached file " + filename); content = content.replaceAll("\"", """); content = content.replaceAll("<", "<"); content = content.replaceAll(">", ">"); content = content.replaceAll("[\\r\\n]+", "<br />"); content = i18nService.getString("TICKET_ACTION.EMAIL_FEED.EXPAND_PART", filename) + content; domainService.giveInformation( null, ticket, content, ActionScope.DEFAULT, false); } /** * Get a text/html part. * @param part * @param ticket * @param partIndex * @param errorHolder */ protected void getHtmlPart( final Part part, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing text/html part " + part + "..."); String content = getPartContentAsString(part, errorHolder); if (!StringUtils.hasText(content)) { return; } byte [] contentByteArray = contentToByteArray(content, errorHolder); if (contentByteArray == null) { return; } String filename = getPartFilename(part, ticket, partIndex, ".html", errorHolder); getDomainService().addFileInfo(new FileInfo( filename, contentByteArray, ticket, null, ActionScope.DEFAULT)); errorHolder.addInfo("added attached file " + filename); MessageHtmlCleaner cleaner = new MessageHtmlCleaner(content); String output; try { cleaner.clean(); output = i18nService.getString( "TICKET_ACTION.EMAIL_FEED.EXPAND_PART", filename) + cleaner.getXmlAsString(); } catch (IOException e) { errorHolder.addInfo("could not clean the HTML: " + e.getMessage()); return; } domainService.giveInformation( null, ticket, output, ActionScope.DEFAULT, false); } /** * Get the content of a non multipart message part. * @param part * @param errorHolder * @return an object. */ protected Object getMiscPartContent( final Part part, final ErrorHolder errorHolder) { Object content = getPartContent(part, errorHolder); if (content == null) { return null; } if (content instanceof String) { return content; } if (content instanceof InputStream) { return content; } errorHolder.addError( "getSimplePartContent(): unexpected class " + content.getClass().getName() + " (" + String.class.getName() + " or " + InputStream.class.getName() + " expected)"); return null; } /** * Get a misc part. * @param part * @param ticket * @param partIndex * @param errorHolder */ protected void getMiscPart( final Part part, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing misc part " + part + "..."); Object content = getMiscPartContent(part, errorHolder); if (content == null) { return; } byte [] contentByteArray = contentToByteArray(content, errorHolder); if (contentByteArray == null) { return; } String filename = getPartFilename(part, ticket, partIndex, ".bin", errorHolder); getDomainService().addFileInfo( new FileInfo(filename, contentByteArray, ticket, null, ActionScope.DEFAULT)); errorHolder.addInfo("added attached file " + filename); } /** * @return the number of parts of a multipart. * @param multipart * @param errorHolder */ protected int getMultipartCount( final MimeMultipart multipart, final ErrorHolder errorHolder) { try { int partCount = multipart.getCount(); errorHolder.addInfo(partCount + " parts found"); return partCount; } catch (MessagingException e) { errorHolder.addError("could not get the number of parts: " + e.getMessage()); return 0; } } /** * Get an alternative multipart. * @param multipart * @param ticket * @param partIndex * @param errorHolder */ protected void getAlternativeParts( final MimeMultipart multipart, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing multipart/alternative " + multipart + "..."); BodyPart textPart = null; BodyPart htmlPart = null; String htmlPartIndex = null; String textPartIndex = null; int partCount = getMultipartCount(multipart, errorHolder); for (int i = partCount - 1; i >= 0; i--) { BodyPart bodyPart = null; try { bodyPart = multipart.getBodyPart(i); } catch (MessagingException e) { errorHolder.addError("could not get part #" + i + ": " + e.getMessage()); } if (bodyPart != null) { try { if (bodyPart.getContentType().toLowerCase().startsWith("text/plain")) { errorHolder.addInfo("found a text/plain part"); textPart = bodyPart; textPartIndex = getPartIndex(partIndex, i); } else if (bodyPart.getContentType().toLowerCase().startsWith("text/html")) { errorHolder.addInfo("found a text/html part"); htmlPart = bodyPart; htmlPartIndex = getPartIndex(partIndex, i); } } catch (MessagingException e) { errorHolder.addError( "could not get the content-type of part #" + i + ": " + e.getMessage()); } } } if (textPart == null && htmlPart == null) { errorHolder.addError( "could not get any text/plain or text/html part multipart/alternative mail"); return; } if (htmlPart != null) { errorHolder.addInfo("adding the text/html part..."); getParts(htmlPart, ticket, htmlPartIndex, errorHolder); } else if (textPart != null) { errorHolder.addInfo("adding the text/plain part..."); getParts(textPart, ticket, textPartIndex, errorHolder); } } /** * Get a mixed multipart. * @param multipart * @param ticket * @param partIndex * @param errorHolder */ protected void getMixedParts( final MimeMultipart multipart, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing multipart/mixed " + multipart + "..."); int partCount = getMultipartCount(multipart, errorHolder); for (int i = partCount - 1; i >= 0; i--) { BodyPart bodyPart = null; try { bodyPart = multipart.getBodyPart(i); } catch (MessagingException e) { errorHolder.addError("could not get part #" + i + ": " + e.getMessage()); } if (bodyPart != null) { errorHolder.addInfo("adding part#" + i + "..."); getParts(bodyPart, ticket, getPartIndex(partIndex, i), errorHolder); } } } /** * Get a signed multipart. * @param multipart * @param ticket * @param partIndex * @param errorHolder */ protected void getSignedParts( final MimeMultipart multipart, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing multipart/signed " + multipart + "..."); int partCount = getMultipartCount(multipart, errorHolder); for (int i = partCount - 1; i >= 0; i--) { BodyPart bodyPart = null; try { bodyPart = multipart.getBodyPart(i); } catch (MessagingException e) { errorHolder.addError("could not get part #" + i + ": " + e.getMessage()); } if (bodyPart != null) { errorHolder.addInfo("adding part#" + i + "..."); getParts(bodyPart, ticket, getPartIndex(partIndex, i), errorHolder); } } } /** * Get a message part. * @param part * @param ticket * @param partIndex * @param errorHolder */ protected void getMessageParts( final Part part, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing message/xxx " + part + "..."); byte [] contentByteArray; if (part instanceof MimeMultipart) { contentByteArray = contentToByteArray(part, errorHolder); if (contentByteArray == null) { return; } } else if (part instanceof IMAPBodyPart) { Object content = getPartContentAsMultipartOrString(part, errorHolder); if (content == null) { return; } contentByteArray = contentToByteArray(content, errorHolder); if (contentByteArray == null) { return; } } else { errorHolder.addError( "getMessagePart(): unexpected class " + part.getClass().getName() + " (" + MimeMultipart.class.getName() + " or " + IMAPBodyPart.class.getName() + " expected)"); return; } String filename = getPartFilename(part, ticket, partIndex, ".elm", errorHolder); getDomainService().addFileInfo(new FileInfo( filename, contentByteArray, ticket, null, ActionScope.DEFAULT)); errorHolder.addInfo("added attached file " + filename); if (part instanceof MimeMultipart) { getMixedParts((MimeMultipart) part, ticket, partIndex, errorHolder); } } /** * Get the parts of a part. * @param part * @param ticket * @param partIndex * @param errorHolder */ protected void getParts( final Part part, final Ticket ticket, final String partIndex, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing multipart " + part + "..."); String contentType = extractContentType(part, errorHolder); if (contentType == null) { return; } if (contentType.toLowerCase().startsWith("multipart/alternative")) { MimeMultipart multipart = getPartContentAsMultipart(part, errorHolder); if (multipart == null) { return; } getAlternativeParts(multipart, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("multipart/mixed") || contentType.toLowerCase().startsWith("multipart/related")) { MimeMultipart multipart = getPartContentAsMultipart(part, errorHolder); if (multipart == null) { return; } getMixedParts(multipart, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("multipart/signed")) { MimeMultipart multipart = getPartContentAsMultipart(part, errorHolder); if (multipart == null) { return; } getSignedParts(multipart, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("multipart/appledouble")) { MimeMultipart multipart = getPartContentAsMultipart(part, errorHolder); if (multipart == null) { return; } getMixedParts(multipart, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("multipart")) { errorHolder.addError("unknown multipart content-type: " + contentType); } else if (contentType.toLowerCase().startsWith("message")) { getMessageParts(part, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("text/plain")) { getTextPart(part, ticket, partIndex, errorHolder); } else if (contentType.toLowerCase().startsWith("text/html")) { getHtmlPart(part, ticket, partIndex, errorHolder); } else { getMiscPart(part, ticket, partIndex, errorHolder); } errorHolder.addInfo("done with the analysis of part " + part + "..."); } /** * Split a message into multiple parts. * @param message * @param ticket * @param errorHolder */ protected void getMessageParts( final Message message, final Ticket ticket, final ErrorHolder errorHolder) { getParts(message, ticket, "", errorHolder); } /** * @see org.esupportail.helpdesk.services.feed.imap.TicketMessageReader#read( * javax.mail.Message, java.lang.String, org.esupportail.helpdesk.domain.beans.Department, * org.esupportail.helpdesk.domain.beans.Category, org.esupportail.helpdesk.services.feed.imap.spam.SpamFilter, * boolean, org.esupportail.helpdesk.domain.beans.Category, org.esupportail.helpdesk.services.feed.ErrorHolder) */ @Override public Ticket read( final Message message, final String address, final Department creationDepartment, final Category category, final SpamFilter spamFilter, final boolean deleteSpam, final Category spamCategory, final ErrorHolder errorHolder) { errorHolder.addInfo("analysing message " + message + "..."); Set<Address> recipients = extractRecipients(message, errorHolder); User sender = extractSender(message, errorHolder); String subject = extractSubject(message, errorHolder); String contentType = extractContentType(message, errorHolder); byte[] data = extractData(message, errorHolder); Ticket ticket = null; if (errorHolder.hasErrors()) { errorHolder.addInfo(errorHolder.getErrorNumber() + " error(s) found, skiping the mail"); } else { boolean spam = spamFilter.isSpam(sender, recipients, subject, contentType, data); if (spam && deleteSpam) { errorHolder.addInfo("message [" + subject + "] from [" + sender + "] was considered as SPAM and deleted"); } else { Category targetCategory; if (spam) { targetCategory = spamCategory; } else { targetCategory = category; } ticket = domainService.addEmailTicket( sender, address, creationDepartment, targetCategory, subject); errorHolder.addInfo("added ticket #" + ticket.getId()); getMessageParts(message, ticket, errorHolder); domainService.addFileInfo(new FileInfo( ticket.getId() + ".elm", data, ticket, sender, ActionScope.DEFAULT)); if (spam) { String msg = "<font color=\"red\">" + "This email was considered as SPAM by the application." + "</font>"; domainService.closeTicket(null, ticket, msg, ActionScope.DEFAULT, false); errorHolder.addInfo("message [" + subject + "] from [" + sender + "] was considered as SPAM and closed"); } } } if (errorHolder.hasErrors()) { errorHolder.addInfo( errorHolder.getErrorNumber() + " error(s) found"); } errorHolder.addInfo("done with message " + message + "..."); if (ticket != null && errorHolder.hasErrors()) { String analysis = ""; for (String string : errorHolder.getStrings()) { analysis += "\n" + string; } analysis = analysis.replaceAll("\"", """); analysis = analysis.replaceAll("<", "<"); analysis = analysis.replaceAll(">", ">"); analysis = analysis.replaceAll("[\\r\\n]+", "<br />"); analysis = i18nService.getString( "TICKET_ACTION.EMAIL_FEED.ERRORS", String.valueOf(errorHolder.getErrorNumber())) + "<font color=\"red\">" + analysis + "</font>"; domainService.giveInformation( null, ticket, analysis, ActionScope.MANAGER, false); } return ticket; } /** * @return the domainService */ protected DomainService getDomainService() { return domainService; } /** * @param domainService the domainService to set */ public void setDomainService(final DomainService domainService) { this.domainService = domainService; } /** * @return the i18nService */ protected I18nService getI18nService() { return i18nService; } /** * @param service the i18nService to set */ public void setI18nService(final I18nService service) { i18nService = service; } }