Mercurial Hosting > nabble
view src/nabble/model/MailMessageFormat.java @ 62:4674ed7d56df default tip
remove n2
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sat, 30 Sep 2023 20:25:29 -0600 |
parents | 7ecd1a4ef557 |
children |
line wrap: on
line source
package nabble.model; import fschmidt.html.Html; import fschmidt.html.HtmlTag; import fschmidt.util.java.HtmlUtils; import fschmidt.util.mail.AlternativeMultipartContent; import fschmidt.util.mail.Content; import fschmidt.util.mail.FileAttachmentContent; import fschmidt.util.mail.HtmlTextContent; import fschmidt.util.mail.Mail; import fschmidt.util.mail.MailAddress; import fschmidt.util.mail.MailException; import fschmidt.util.mail.MailHome; import fschmidt.util.mail.MailParseException; import fschmidt.util.mail.MultipartContent; import fschmidt.util.mail.TextContent; import nabble.view.lib.Permissions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.regex.Matcher; /** * User: dv * Date: Dec 29, 2007 * Time: 4:27:45 PM */ public final class MailMessageFormat extends Message.Format { private static final Logger logger = LoggerFactory.getLogger(MailMessageFormat.class); MailMessageFormat(char code, String name) { super(code, name); } protected final Html parse(String msg,Message.Source source) { try { return doParse(msg,source); } catch(MailParseException e) { logger.error(e.toString()); return new Html(); } } protected final Html parseForMailText(String msg,Message.Source source) { throw new UnsupportedOperationException(); } private static Html getTextContentAsHtml(Content content) { String s = getTextContent(content); return s==null ? null : textToHtml(s); } private static Html getHtmlContentAsHtml(Content content,Message.Source source) { String s = getHtmlContent(content); if( s == null ) return null; Html html = new Html(); html.removeBadTags(true); html.parse(s); if( source != null ) { markManualQuotes(html); } return html; } private static Html getMailAsHtml(Mail mail,Message.Source source) { Content content = mail.getContent(); Html list; if (preferTextContent(mail)) { list = getTextContentAsHtml(content); if( list == null ) list = getHtmlContentAsHtml(content, source); } else { list = getHtmlContentAsHtml(content, source); if( list == null ) list = getTextContentAsHtml(content); } return list; } private static Html doParse(String msg, Message.Source source) { Mail mail = MailHome.newMail(msg); Html list = getMailAsHtml(mail, source); if( list == null ) list = new Html(); List<FileAttachmentContent> attachments = getAttachments(mail); setImgAttachmentSrc(list, attachments, source); tagEmails(list,(Node)source); list.addAll( new Html(listAttachmentsHtml(attachments, source)) ); return list; } boolean isOk(String message) { try { doParse(message,null); // make sure we can return true; } catch(MailParseException e) { logger.warn("",e); return false; } } protected String getText(String msg, Message.Source source) { try { // basic text message String text = getMailText(msg,source); // append attachments Mail mail = MailHome.newMail(msg); List<FileAttachmentContent> attachments = getAttachments(mail); String attachmentText = listAttachmentsText(attachments, source); if (attachmentText.length() > 0) { text = text + "\n\n" + attachmentText; } return text; } catch(MailParseException e) { logger.error(e.toString()); return ""; } } String getMailText(String msg, Message.Source source) { Mail mail = MailHome.newMail(msg); Content content = mail.getContent(); String s = getTextContent(content); if (s == null) { s = getHtmlContent(content); if (s == null) s = ""; s = htmlToText(s); } return s; } protected final String getTextWithoutQuotes(String msg, Message.Source source) { return getText(msg,source); } protected final String getEmail(String msg, int i) { Mail mail = MailHome.newMail(msg); Html list = getMailAsHtml(mail, null); tagEmails(list); return MessageFormatImpls.getEmail(list, i); } /** * Process html and replace source url in img tags with a new Nabble-specific attachment url * * @param list html to process * @param attachments list of attachments * @param source source */ private static void setImgAttachmentSrc(Html list, List<FileAttachmentContent> attachments, Message.Source source) { if (source instanceof NodeImpl) { FileAttachmentContent[] fa = attachments.toArray(new FileAttachmentContent[attachments.size()]); for (ListIterator i = list.listIterator(); i.hasNext();) { Object o = i.next(); if (o instanceof HtmlTag) { HtmlTag tag = (HtmlTag) o; if (tag.getName().toLowerCase().equals("img")) { String src = HtmlTag.unquote(tag.getAttributeValue("src")); if (src == null || !src.startsWith("cid:")) continue; src = src.substring(4); int n = 0; FileAttachmentContent attachment = null; for (; n < fa.length; n++) { String contentId = fa[n].getContentID(); if (contentId!=null && src.equals(MailSubsystem.stripBrackets(contentId))) { attachment = fa[n]; attachments.set(n, null); break; } } if (attachment != null) { String fileName = getFileName(attachment, n); StringBuilder buf = new StringBuilder(); buf.append("\""); buf.append(source.getSite().getBaseUrl()); buf.append("/attachment/"); buf.append(source.getSourceId()); buf.append("/"); buf.append(n); buf.append("/"); buf.append(HtmlUtils.urlEncode(fileName).replaceAll("\\+", "%20")); buf.append("\""); tag.setAttribute("src", buf.toString()); } else { i.remove(); } } } } } } /** * Create string representation to an attachment list * * @param attachments list of attachments * @param source source * @return s with attachment list */ private static String listAttachmentsHtml(List<FileAttachmentContent> attachments, Message.Source source) { if (attachments.size() == 0 || !(source instanceof NodeImpl)) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < attachments.size(); i++) { if (attachments.get(i) == null) continue; String fileName = getFileName(attachments.get(i), i); String fileSize = getFileSize(attachments.get(i)); buf.append("<br/><img src=\""); buf.append(source.getSite().getBaseUrl()); buf.append("/images/icon_attachment.gif\" > <strong>"); buf.append(fileName); buf.append("</strong>"); if (fileSize != null) { buf.append(" ("); buf.append(fileSize); buf.append(")"); } buf.append(" <a href=\""); buf.append(source.getSite().getBaseUrl()); buf.append("/attachment/"); buf.append(source.getSourceId()); buf.append("/"); buf.append(i); buf.append("/"); buf.append(HtmlUtils.urlEncode(fileName).replaceAll("\\+", "%20")); buf.append("\" target=\"_top\">"); buf.append("Download Attachment"); buf.append("</a>"); } if (buf.length() == 0) return ""; buf.insert(0, "<!--start-attachments--><div class=\"small\">"); buf.append("</div><!--end-attachments-->"); return buf.toString(); } /** * Create string representation to an attachment list * * @param attachments list of attachments * @param source source * @return s with attachment list */ private static String listAttachmentsText(List<FileAttachmentContent> attachments, Message.Source source) { if (attachments.size() == 0 || !(source instanceof NodeImpl)) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < attachments.size(); i++) { if (attachments.get(i) == null) continue; String fileName = getFileName(attachments.get(i), i); String fileSize = getFileSize(attachments.get(i)); buf.append(fileName); if (fileSize != null) { buf.append(" ("); buf.append(fileSize); buf.append(")"); } buf.append(" <"); buf.append(source.getSite().getBaseUrl()); buf.append("/attachment/"); buf.append(source.getSourceId()); buf.append("/"); buf.append(i); buf.append("/"); buf.append(HtmlUtils.urlEncode(fileName).replaceAll("\\+", "%20")); buf.append(">\n"); } if (buf.length() == 0) return ""; return buf.toString(); } /** * Get a name of a file in the attachment * * @param attachment attachment with file * @param n suffix for name * @return the name of the file in the attachment */ private static String getFileName(FileAttachmentContent attachment, int n) { try { if (attachment.getFileName() != null) return attachment.getFileName(); } catch (MailException e) { } try { if (attachment.getContentID() != null) return MailSubsystem.stripBrackets(attachment.getContentID()); } catch (MailException e) { } return "attachment" + n; } /** * Get a size of a file in the attachment * * @param attachment attachment with file * @return symbolic file size presentation or null */ private static String getFileSize(FileAttachmentContent attachment) { int size = attachment.getSize(); if( size == -1 ) return null; if (size > 1024 * 1024) return size / (1024 * 1024) + "M"; else if (size > 1024) return size / 1024 + "K"; else return size + " bytes"; } /** * Get attachment as a stream * * @param post attachment source * @param i index of the attachment * @return a stream with attachment's content */ public InputStream getAttachment(Node post, int i) { String msg = post.getMessage().getRaw(); Mail mail = MailHome.newMail(msg); List<FileAttachmentContent> attachments = getAttachments(mail); FileAttachmentContent attachment = attachments.get(i); return attachment.getInputStream(); } /** * Extract html content from content object * * @param content content to analyze * @return string representation of content */ private static String getHtmlContent(Content content) { if (content instanceof HtmlTextContent) { HtmlTextContent html = (HtmlTextContent) content; return html.getText(); } else if (content instanceof AlternativeMultipartContent) { AlternativeMultipartContent amp = (AlternativeMultipartContent) content; Content[] amparts = amp.getParts(); for (Content ampart : amparts) { String ams = getHtmlContent(ampart); if (ams != null) return ams; } return null; } else if (content instanceof MultipartContent) { MultipartContent mmp = (MultipartContent) content; Content[] mmparts = mmp.getParts(); StringBuilder buf = new StringBuilder(); boolean hasContent = false; for (Content mmpart : mmparts) { String mms = getHtmlContent(mmpart); if (mms == null) { mms = getTextContent(mmpart); if (mms != null) mms = textToHtml(mms).toString(); } if (mms != null) { hasContent = true; if (buf.length() > 0) buf.append("<br />"); buf.append(mms); } } return hasContent ? buf.toString() : null; } else if (content instanceof Mail) { return getHtmlContent(((Mail) content).getContent()); } else { return null; } } /** * Does a source prefer text content * @return true if text content is prefered */ private static boolean preferTextContent(Mail mail) { String returnPath = MailSubsystem.getReturnPath(mail); return returnPath!=null && returnPath.endsWith(".groups.yahoo.com"); } /** * Extract text content from content object * * @param content content to analyze * @return string representation of content */ private static String getTextContent(Content content) { if (content instanceof AlternativeMultipartContent) { AlternativeMultipartContent amp = (AlternativeMultipartContent) content; Content[] amparts = amp.getParts(); for (Content ampart : amparts) { String ams = getTextContent(ampart); if (ams != null) return ams; } return null; } else if (content instanceof MultipartContent) { MultipartContent mmp = (MultipartContent) content; Content[] mmparts = mmp.getParts(); StringBuilder buf = new StringBuilder(); boolean hasContent = false; for (Content mmpart : mmparts) { String mms = getTextContent(mmpart); if (mms == null) { mms = getHtmlContent(mmpart); if (mms != null) mms = htmlToText(mms); } if (mms != null) { hasContent = true; if (buf.length() > 0) buf.append('\n'); buf.append(mms); } } return hasContent ? buf.toString() : null; } else if (content instanceof TextContent && !(content instanceof HtmlTextContent)) { TextContent textContent = (TextContent) content; return textContent.getText(); } else if (content instanceof Mail) { return getTextContent(((Mail) content).getContent()); } else { return null; } } private static List<FileAttachmentContent> getAttachments(Content content) { if (content instanceof FileAttachmentContent) { return Collections.singletonList( (FileAttachmentContent)content ); } if (content instanceof MultipartContent) { List<FileAttachmentContent> attachments = new ArrayList<FileAttachmentContent>(); MultipartContent mmp = (MultipartContent) content; for (Content mmpart : mmp.getParts()) { attachments.addAll( getAttachments(mmpart) ); } return attachments; } if (content instanceof Mail) { return getAttachments( ((Mail)content).getContent() ); } return Collections.emptyList(); } private static Html textToHtml(String text) { Html list = MessageFormatImpls.convertLinks(text); for( ListIterator<Object> iter = list.listIterator(); iter.hasNext(); ) { Object obj = iter.next(); if( obj instanceof String ) { String s = (String)obj; s = HtmlUtils.htmlEncode(s); iter.set(s); } } MessageFormatImpls.textToHtml(list); markManualQuotes(list); return list; } private static final HtmlTag emailTag = new HtmlTag("email"); private static final HtmlTag _emailTag = new HtmlTag("/email"); /** * Use <email>me@host.com</email> to replace * email. MessageFormatImpls.processEmail should be called to touch up the final result */ public static void tagEmails(Html list,Node node) { if( node!=null && !Permissions.isPrivate(node) ) tagEmails(list); } private static void tagEmails(Html list) { boolean insideA = false; boolean insideEmail = false; for( ListIterator<Object> i=list.listIterator(); i.hasNext(); ) { Object o = i.next(); if( o instanceof HtmlTag ) { HtmlTag tag = (HtmlTag)o; String tagName = tag.getName().toLowerCase(); if( insideEmail ) { if( tagName.equals("/email") ) insideEmail = false; continue; } if( tagName.equals("email") ) { insideEmail = true; } else if( tagName.equals("a") ) { String href = tag.getAttributeValue("href"); if (href != null) { if (href.startsWith("\"mailto:")) { i.remove(); boolean foundCloseTag = false; while (i.hasNext() && !foundCloseTag) { Object o1 = i.next(); if ( o1 instanceof HtmlTag ) { HtmlTag tag1 = (HtmlTag)o1; if (tag1.getName().toLowerCase().equals("/a")) { foundCloseTag = true; } } i.remove(); } i.add(emailTag); i.add(href.substring(8, href.length() - 1)); i.add(_emailTag); } else { insideA = true; } } } else if (tagName.equals("/a")) { insideA = false; } } else if (o instanceof String && !insideEmail) { String s = (String)o; Matcher m = MailAddress.EMAIL_PATTERN.matcher(s); if( m.find() ) { StringBuffer buf = new StringBuffer(); do { m.appendReplacement(buf, insideA?"$1@...":"<email>$1@$2</email>"); } while (m.find()); m.appendTail(buf); Html hlist = new Html(buf.toString()); i.remove(); for(ListIterator j=hlist.listIterator();j.hasNext();) { i.add(j.next()); } } } } } private static final HtmlTag divHiddenQuote = new HtmlTag("div class='shrinkable-quote'"); private static final HtmlTag _div = new HtmlTag("/div"); private static void markManualQuotes(Html list) { int n = list.size() - 1; if( n==-1 ) return; int nLines = 0; int iStart = -1; Object o = list.get(0); if( o instanceof String ) { String s = (String)o; if( s.startsWith(">") ) { nLines++; } } for( int i=0; i<n; i++ ) { o = list.get(i); if( !(o instanceof HtmlTag) ) continue; HtmlTag tag = (HtmlTag)o; if( !tag.getName().toLowerCase().equals("br") ) continue; o = list.get(i+1); if( o instanceof String ) { String s = (String)o; if( s.startsWith(">") ) { if( nLines == 0 ) iStart = i; nLines++; continue; } } if( nLines >= Init.quotedLinesToHide ) { if( iStart == -1 ) { list.add(0,divHiddenQuote); } else { list.add(iStart,divHiddenQuote); } i++; n++; list.set(i,_div); } nLines = 0; } if( nLines >= Init.quotedLinesToHide ) { if( iStart == -1 ) iStart = 0; list.add(iStart,divHiddenQuote); list.add(_div); } } private static String htmlToText(String msg) { return MessageUtils.htmlToText(new Html(msg)); } }