Author: bago
Date: Sun Sep 10 15:35:06 2006
New Revision: 442021
URL: http://svn.apache.org/viewvc?view=rev&rev=442021
Log:
Added mailets to handle plain text mails.
WrapText/UnwrapText are used to remove/apply wrapping/flowing (JAMES-532)
OnlyText is used to extract the text plain part of a message (or convert the
html part to text only (JAMES-533)
ReplaceContent is used to replace patterns (via regex) in subject or plain text
content and being able to specify an output encoding (JAMES-534)
Added:
james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java
(with props)
james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java
(with props)
james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java
(with props)
james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java
(with props)
james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java
(with props)
james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java
(with props)
Added:
james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java?view=auto&rev=442021
==============================================================================
---
james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java
(added)
+++
james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,284 @@
+package org.apache.james.transport.mailets;
+
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetException;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Part;
+import javax.mail.internet.ContentType;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Keep only the text part of a message.
+ * <p>If the message is text only then it doesn't touch it, if it is a
multipart it
+ * transform it a in plain text message with the first text part found.
+ * - text/plain
+ * - text/html => with a conversion to text only
+ * - text/* as is.</p>
+ */
+public class OnlyText extends GenericMailet {
+ private static final String PARAMETER_NAME_NOTEXT_PROCESSOR =
"NoTextProcessor";
+
+ private String optionsNotextProcessor = null;
+ private final HashMap charMap = new HashMap();
+
+ /**
+ * returns a String describing this mailet.
+ *
+ * @return A desciption of this mailet
+ */
+ public String getMailetInfo() {
+ return "OnlyText";
+ }
+
+ public void init() throws MailetException {
+ optionsNotextProcessor =
getInitParameter(PARAMETER_NAME_NOTEXT_PROCESSOR);
+ initEntityTable();
+ }
+
+ private int[] process(Mail mail, Multipart mp, int found, int htmlPart,
int stringPart) throws MessagingException, IOException {
+ for (int i = 0; found < 0 && i < mp.getCount(); i++) {
+ Object content = null;
+ try {
+ content = mp.getBodyPart(i).getContent();
+ } catch (java.io.UnsupportedEncodingException e) {
+ log("Caught error [" + e.getMessage() + "] in a
text/plain part, skipping...");
+ }
+ if (content != null) {
+ if (mp.getBodyPart(i).isMimeType("text/plain"))
{
+ setContentFromPart(mail.getMessage(),
mp.getBodyPart(i), null, false);
+ found = 1;
+ }
+ else if (htmlPart == -1 &&
mp.getBodyPart(i).isMimeType("text/html"))
+ htmlPart = i;
+
+ else if (stringPart == -1 && content instanceof
String)
+ stringPart = i;
+
+ else if (content instanceof Multipart) {
+ int[] res = process(mail, (Multipart)
content, found, htmlPart, stringPart);
+ found = res[0];
+ htmlPart = res[1];
+ stringPart = res[2];
+ }
+ }
+ }
+
+ return new int[] {found, htmlPart, stringPart};
+
+ }
+
+ public void service(Mail mail) throws MailetException {
+ try {
+ Object content = mail.getMessage().getContent();
+ if (content instanceof Multipart) {
+ Multipart mp = (Multipart) content;
+
+ int found = -1;
+ int htmlPart = -1;
+ int stringPart = -1;
+ int[] res = process(mail, (Multipart) content,
found, htmlPart, stringPart);
+ found = res[0];
+ htmlPart = res[1];
+ stringPart = res[2];
+
+ if (found < 0 && htmlPart != -1) {
+ setContentFromPart(mail.getMessage(),
mp.getBodyPart(htmlPart), html2Text((String)
mp.getBodyPart(htmlPart).getContent()), true);
+ found = 1;
+ }
+
+ if (found < 0 && stringPart != -1) {
+ setContentFromPart(mail.getMessage(),
mp.getBodyPart(htmlPart), null, false);
+ found = 1;
+ }
+
+
+ if (found < 0 && optionsNotextProcessor !=
null) mail.setState(optionsNotextProcessor);
+
+ }
+
+ else if (!(content instanceof String) &&
optionsNotextProcessor != null) mail.setState(optionsNotextProcessor);
+
+ else if (mail.getMessage().isMimeType("text/html")) {
+ setContentFromPart(mail.getMessage(),
mail.getMessage(), html2Text((String) mail.getMessage().getContent()), true);
+ }
+
+ } catch (IOException e) {
+ throw new MailetException("Failed fetching text part",
e);
+
+ } catch (MessagingException e) {
+ throw new MailetException("Failed fetching text part",
e);
+ }
+ }
+
+ private static void setContentFromPart(Message m, Part p, String
newText, boolean setTextPlain) throws MessagingException, IOException {
+ String contentType = p.getContentType();
+ if (setTextPlain) {
+ ContentType ct = new ContentType(contentType);
+ ct.setPrimaryType("text");
+ ct.setSubType("plain");
+ contentType = ct.toString();
+ }
+ m.setContent(newText != null ? newText : p.getContent(),
contentType);
+ String[] h = p.getHeader("Content-Transfer-Encoding");
+ if (h != null && h.length > 0)
m.setHeader("Content-Transfer-Encoding", h[0]);
+ m.saveChanges();
+ }
+
+ public String html2Text(String html) {
+ return decodeEntities(html
+ .replaceAll("\\<([bB][rR]|[dD][lL])[ ]*[/]*[ ]*\\>",
"\n")
+
.replaceAll("\\</([pP]|[hH]5|[dD][tT]|[dD][dD]|[dD][iI][vV])[ ]*\\>", "\n")
+ .replaceAll("\\<[lL][iI][ ]*[/]*[ ]*\\>", "\n* ")
+ .replaceAll("\\<[dD][dD][ ]*[/]*[ ]*\\>", " - ")
+ .replaceAll("\\<.*?\\>", ""));
+ }
+
+ public String decodeEntities(String data) {
+ StringBuffer buffer = new StringBuffer();
+ StringBuffer res = new StringBuffer();
+ int lastAmp = -1;
+ for (int i = 0; i < data.length(); i++) {
+ char c = data.charAt(i);
+
+ if (c == '&' && lastAmp == -1) lastAmp =
buffer.length();
+ else if (c == ';' && (lastAmp > -1)) { // && (lastAmp >
(buffer.length() - 7))) { // max: &#xxxx;
+ if (charMap.containsKey(buffer.toString()))
res.append((String) charMap.get(buffer.toString()));
+ else res.append("&" + buffer.toString() + ";");
+ lastAmp = -1;
+ buffer = new StringBuffer();
+ }
+ else if (lastAmp == -1) res.append(c);
+ else buffer.append(c);
+ }
+ return res.toString();
+ }
+
+ private final void initEntityTable() {
+ for (int index = 11; index < 32; index++) charMap.put("#0" +
index, String.valueOf((char) index));
+ for (int index = 32; index < 128; index++) charMap.put("#" +
index, String.valueOf((char) index));
+ for (int index = 128; index < 256; index++) charMap.put("#" +
index, String.valueOf((char) index));
+
+ // una buona tabella è qui
+ //
http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
+
+ charMap.put("#09", "\t");
+ charMap.put("#10", "\n");
+ charMap.put("#13", "\r");
+ charMap.put("#60", "<");
+ charMap.put("#62", ">");
+
+ charMap.put("lt", "<");
+ charMap.put("gt", ">");
+ charMap.put("amp", "&");
+ charMap.put("nbsp", " ");
+ charMap.put("quot", "\"");
+
+ charMap.put("iexcl", "\u00A1");
+ charMap.put("cent", "\u00A2");
+ charMap.put("pound", "\u00A3");
+ charMap.put("curren", "\u00A4");
+ charMap.put("yen", "\u00A5");
+ charMap.put("brvbar", "\u00A6");
+ charMap.put("sect", "\u00A7");
+ charMap.put("uml", "\u00A8");
+ charMap.put("copy", "\u00A9");
+ charMap.put("ordf", "\u00AA");
+ charMap.put("laquo", "\u00AB");
+ charMap.put("not", "\u00AC");
+ charMap.put("shy", "\u00AD");
+ charMap.put("reg", "\u00AE");
+ charMap.put("macr", "\u00AF");
+ charMap.put("deg", "\u00B0");
+ charMap.put("plusmn", "\u00B1");
+ charMap.put("sup2", "\u00B2");
+ charMap.put("sup3", "\u00B3");
+
+ charMap.put("acute", "\u00B4");
+ charMap.put("micro", "\u00B5");
+ charMap.put("para", "\u00B6");
+ charMap.put("middot", "\u00B7");
+ charMap.put("cedil", "\u00B8");
+ charMap.put("sup1", "\u00B9");
+ charMap.put("ordm", "\u00BA");
+ charMap.put("raquo", "\u00BB");
+ charMap.put("frac14", "\u00BC");
+ charMap.put("frac12", "\u00BD");
+ charMap.put("frac34", "\u00BE");
+ charMap.put("iquest", "\u00BF");
+
+ charMap.put("Agrave", "\u00C0");
+ charMap.put("Aacute", "\u00C1");
+ charMap.put("Acirc", "\u00C2");
+ charMap.put("Atilde", "\u00C3");
+ charMap.put("Auml", "\u00C4");
+ charMap.put("Aring", "\u00C5");
+ charMap.put("AElig", "\u00C6");
+ charMap.put("Ccedil", "\u00C7");
+ charMap.put("Egrave", "\u00C8");
+ charMap.put("Eacute", "\u00C9");
+ charMap.put("Ecirc", "\u00CA");
+ charMap.put("Euml", "\u00CB");
+ charMap.put("Igrave", "\u00CC");
+ charMap.put("Iacute", "\u00CD");
+ charMap.put("Icirc", "\u00CE");
+ charMap.put("Iuml", "\u00CF");
+
+ charMap.put("ETH", "\u00D0");
+ charMap.put("Ntilde", "\u00D1");
+ charMap.put("Ograve", "\u00D2");
+ charMap.put("Oacute", "\u00D3");
+ charMap.put("Ocirc", "\u00D4");
+ charMap.put("Otilde", "\u00D5");
+ charMap.put("Ouml", "\u00D6");
+ charMap.put("times", "\u00D7");
+ charMap.put("Oslash", "\u00D8");
+ charMap.put("Ugrave", "\u00D9");
+ charMap.put("Uacute", "\u00DA");
+ charMap.put("Ucirc", "\u00DB");
+ charMap.put("Uuml", "\u00DC");
+ charMap.put("Yacute", "\u00DD");
+ charMap.put("THORN", "\u00DE");
+ charMap.put("szlig", "\u00DF");
+
+ charMap.put("agrave", "\u00E0");
+ charMap.put("aacute", "\u00E1");
+ charMap.put("acirc", "\u00E2");
+ charMap.put("atilde", "\u00E3");
+ charMap.put("auml", "\u00E4");
+ charMap.put("aring", "\u00E5");
+ charMap.put("aelig", "\u00E6");
+ charMap.put("ccedil", "\u00E7");
+ charMap.put("egrave", "\u00E8");
+ charMap.put("eacute", "\u00E9");
+ charMap.put("ecirc", "\u00EA");
+ charMap.put("euml", "\u00EB");
+ charMap.put("igrave", "\u00EC");
+ charMap.put("iacute", "\u00ED");
+ charMap.put("icirc", "\u00EE");
+ charMap.put("iuml", "\u00EF");
+
+ charMap.put("eth", "\u00F0");
+ charMap.put("ntilde", "\u00F1");
+ charMap.put("ograve", "\u00F2");
+ charMap.put("oacute", "\u00F3");
+ charMap.put("ocirc", "\u00F4");
+ charMap.put("otilde", "\u00F5");
+ charMap.put("ouml", "\u00F6");
+ charMap.put("divid", "\u00F7");
+ charMap.put("oslash", "\u00F8");
+ charMap.put("ugrave", "\u00F9");
+ charMap.put("uacute", "\u00FA");
+ charMap.put("ucirc", "\u00FB");
+ charMap.put("uuml", "\u00FC");
+ charMap.put("yacute", "\u00FD");
+ charMap.put("thorn", "\u00FE");
+ charMap.put("yuml", "\u00FF");
+ charMap.put("euro", "\u0080");
+ }
+}
Propchange:
james/server/trunk/src/java/org/apache/james/transport/mailets/OnlyText.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java?view=auto&rev=442021
==============================================================================
---
james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java
(added)
+++
james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,319 @@
+package org.apache.james.transport.mailets;
+
+import org.apache.james.util.mailet.StringUtils;
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.ContentType;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Replace text contents
+ * <p>This mailet allow to specific regular expression to replace text in
subject and content.
+ *
+ * Each expression is defined as:
+ * /REGEX_PATTERN/SUBSTITUTION_PATTERN/FLAGS/
+ *
+ * - REGEX_PATTERN is a regex used for the match
+ * - SUBSTITUTION_PATTERN is a substitution pattern
+ * - FLAGS flags supported for the pattern:
+ * i: case insensitive
+ * m: multi line
+ * x: extended (N/A)
+ * r: repeat - keep matching until a substitution is possible
+ *
+ * To identify subject and body pattern we use the tags <subjectPattern>
and <bodyPattern>
+ *
+ * Rules can be specified in external files.
+ * Lines must be CRLF terminated and lines starting with # are considered
commments.
+ * Tags used to include external files are <subjectPatternFile> and
+ * <bodyPatternFile>
+ * If file path starts with # then the file is loaded as a reasource.
+ *
+ * Use of both files and direct patterns at the same time is allowed.
+ *
+ * This mailet allow also to enforce the resulting charset for messages
processed.
+ * To do that the tag <charset> must be specified.
+ *
+ * NOTE:
+ * Regexp rules must be escaped by regexp excaping rules and applying this 2
additional rules:
+ * - "/" char inside an expression must be prefixed with "\":
+ * e.g: "/\//-//" replaces "/" with "-"
+ * - when the rules are specified using <subjectPattern> or
<bodyPattern> and
+ * "/,/" has to be used in a pattern string it must be prefixed with a "\".
+ * E.g: "/\/\/,//" replaces "/" with "," (the rule would be "/\//,//" but
the "/,/" must
+ * be escaped.
+ */
+public class ReplaceContent extends GenericMailet {
+ private static final String PARAMETER_NAME_SUBJECT_PATTERN =
"subjectPattern";
+ private static final String PARAMETER_NAME_BODY_PATTERN = "bodyPattern";
+ private static final String PARAMETER_NAME_SUBJECT_PATTERNFILE =
"subjectPatternFile";
+ private static final String PARAMETER_NAME_BODY_PATTERNFILE =
"bodyPatternFile";
+ private static final String PARAMETER_NAME_CHARSET = "charset";
+
+ public static final int FLAG_REPEAT = 1;
+
+ private Pattern[] subjectPatterns;
+ private String[] subjectSubstitutions;
+ private Integer[] subjectFlags;
+ private Pattern[] bodyPatterns;
+ private String[] bodySubstitutions;
+ private Integer[] bodyFlags;
+ private String charset;
+ private int debug = 0;
+
+ /**
+ * returns a String describing this mailet.
+ *
+ * @return A desciption of this mailet
+ */
+ public String getMailetInfo() {
+ return "ReplaceContent";
+ }
+
+ /**
+ * @return an array containing Pattern and Substitution of the input
stream
+ * @throws MailetException
+ */
+ protected static Object[] getPattern(String line) throws
MailetException {
+ String[] pieces = StringUtils.split(line, "/");
+ if (pieces.length < 3) throw new MailetException("Invalid
expression: " + line);
+ int options = 0;
+ //if (pieces[2].indexOf('x') >= 0) options += Pattern.EXTENDED;
+ if (pieces[2].indexOf('i') >= 0) options +=
Pattern.CASE_INSENSITIVE;
+ if (pieces[2].indexOf('m') >= 0) options += Pattern.MULTILINE;
+ if (pieces[2].indexOf('s') >= 0) options += Pattern.DOTALL;
+
+ int flags = 0;
+ if (pieces[2].indexOf('r') >= 0) flags += FLAG_REPEAT;
+
+ if (pieces[1].indexOf("\\r") >= 0) pieces[1] =
pieces[1].replaceAll("\\\\r", "\r");
+ if (pieces[1].indexOf("\\n") >= 0) pieces[1] =
pieces[1].replaceAll("\\\\n", "\n");
+ if (pieces[1].indexOf("\\t") >= 0) pieces[1] =
pieces[1].replaceAll("\\\\t", "\t");
+
+ return new Object[] {Pattern.compile(pieces[0], options),
pieces[1] , new Integer(flags)};
+ }
+
+ protected static List[] getPatternsFromString(String pattern) throws
MailetException {
+ pattern = pattern.trim();
+ if (pattern.length() < 2 && !pattern.startsWith("/") &&
!pattern.endsWith("/")) throw new MailetException("Invalid parameter value: " +
PARAMETER_NAME_SUBJECT_PATTERN);
+ pattern = pattern.substring(1, pattern.length() - 1);
+ String[] patternArray = StringUtils.split(pattern, "/,/");
+
+ List patterns = new ArrayList();
+ List substitutions = new ArrayList();
+ List flags = new ArrayList();
+ for (int i = 0; i < patternArray.length; i++) {
+ Object[] o = getPattern(patternArray[i]);
+ patterns.add(o[0]);
+ substitutions.add(o[1]);
+ flags.add(o[2]);
+ }
+
+ return new List[] {patterns, substitutions, flags};
+ }
+
+ protected static List[] getPatternsFromStream(InputStream stream,
String charset) throws MailetException, IOException {
+ List patterns = new ArrayList();
+ List substitutions = new ArrayList();
+ List flags = new ArrayList();
+ BufferedReader reader = new BufferedReader(charset != null ?
new InputStreamReader(stream, charset) : new InputStreamReader(stream));
+ //BufferedWriter writer = new BufferedWriter(new
OutputStreamWriter(new FileOutputStream("q:\\correzioniout"), "utf-8"));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.length() > 0 && !line.startsWith("#")) {
+ if (line.length() < 2 && !line.startsWith("/")
&& !line.endsWith("/")) throw new MailetException("Invalid expression: " +
line);
+ Object[] o = getPattern(line.substring(1,
line.length() - 1));
+ patterns.add(o[0]);
+ substitutions.add(o[1]);
+ flags.add(o[2]);
+ }
+ }
+ reader.close();
+ return new List[] {patterns, substitutions, flags};
+ }
+
+ /**
+ * @param filepar File path list (or resources if the path starts with
#) comma separated
+ */
+ private List[] getPatternsFromFileList(String filepar) throws
MailetException, IOException {
+ List patterns = new ArrayList();
+ List substitutions = new ArrayList();
+ List flags = new ArrayList();
+ String[] files = filepar.split(",");
+ for (int i = 0; i < files.length; i++) {
+ files[i] = files[i].trim();
+ if (debug > 0) log("Loading patterns from: " +
files[i]);
+ String charset = null;
+ int pc = files[i].lastIndexOf('?');
+ if (pc >= 0) {
+ charset = files[i].substring(pc + 1);
+ files[i] = files[i].substring(0, pc);
+ }
+ InputStream is = null;
+ if (files[i].startsWith("#")) is =
getClass().getResourceAsStream(files[i].substring(1));
+ else {
+ File f = new File(files[i]);
+ if (f.isFile()) is = new FileInputStream(f);
+ }
+ if (is != null) {
+ List[] o = getPatternsFromStream(is, charset);
+ patterns.addAll(o[0]);
+ substitutions.addAll(o[1]);
+ flags.addAll(o[2]);
+ is.close();
+ }
+ }
+ return new List[] {patterns, substitutions, flags};
+ }
+
+ protected static String applyPatterns(Pattern[] patterns, String[]
substitutions, Integer[] pflags, String text, int debug, GenericMailet
logOwner) {
+ for (int i = 0; i < patterns.length; i ++) {
+ int flags = pflags[i].intValue();
+ boolean changed = false;
+ do {
+ changed = false;
+ String replaced =
patterns[i].matcher(text).replaceAll(substitutions[i]);
+ if (!replaced.equals(text)) {
+ if (debug > 0) logOwner.log("Subject
rule match: " + patterns[i].pattern());
+ text = replaced;
+ changed = true;
+ }
+ } while ((flags & FLAG_REPEAT) > 0 && changed);
+ }
+
+ return text;
+ }
+
+
+ public void init() throws MailetException {
+ charset = getInitParameter(PARAMETER_NAME_CHARSET);
+ debug = Integer.parseInt(getInitParameter("debug", "0"));
+ }
+
+ private void initPatterns() throws MailetException {
+ try {
+ List bodyPatternsList = new ArrayList();
+ List bodySubstitutionsList = new ArrayList();
+ List bodyFlagsList = new ArrayList();
+ List subjectPatternsList = new ArrayList();
+ List subjectSubstitutionsList = new ArrayList();
+ List subjectFlagsList = new ArrayList();
+
+ String pattern =
getInitParameter(PARAMETER_NAME_SUBJECT_PATTERN);
+ if (pattern != null) {
+ List[] o = getPatternsFromString(pattern);
+ subjectPatternsList.addAll(o[0]);
+ subjectSubstitutionsList.addAll(o[1]);
+ subjectFlagsList.addAll(o[2]);
+ }
+
+ pattern = getInitParameter(PARAMETER_NAME_BODY_PATTERN);
+ if (pattern != null) {
+ List[] o = getPatternsFromString(pattern);
+ bodyPatternsList.addAll(o[0]);
+ bodySubstitutionsList.addAll(o[1]);
+ bodyFlagsList.addAll(o[2]);
+ }
+
+ String filepar =
getInitParameter(PARAMETER_NAME_SUBJECT_PATTERNFILE);
+ if (filepar != null) {
+ List[] o = getPatternsFromFileList(filepar);
+ subjectPatternsList.addAll(o[0]);
+ subjectSubstitutionsList.addAll(o[1]);
+ subjectFlagsList.addAll(o[2]);
+ }
+
+ filepar =
getInitParameter(PARAMETER_NAME_BODY_PATTERNFILE);
+ if (filepar != null) {
+ List[] o = getPatternsFromFileList(filepar);
+ bodyPatternsList.addAll(o[0]);
+ bodySubstitutionsList.addAll(o[1]);
+ bodyFlagsList.addAll(o[2]);
+ }
+
+ subjectPatterns = (Pattern[])
subjectPatternsList.toArray(new Pattern[0]);
+ subjectSubstitutions = (String[])
subjectSubstitutionsList.toArray(new String[0]);
+ subjectFlags = (Integer[]) subjectFlagsList.toArray(new
Integer[0]);
+ bodyPatterns = (Pattern[]) bodyPatternsList.toArray(new
Pattern[0]);
+ bodySubstitutions = (String[])
bodySubstitutionsList.toArray(new String[0]);
+ bodyFlags = (Integer[]) bodyFlagsList.toArray(new
Integer[0]);
+
+ } catch (FileNotFoundException e) {
+ throw new MailetException("Failed initialization", e);
+
+ } catch (MailetException e) {
+ throw new MailetException("Failed initialization", e);
+
+ } catch (IOException e) {
+ throw new MailetException("Failed initialization", e);
+
+ }
+ }
+
+ public void service(Mail mail) throws MailetException {
+ initPatterns();
+
+ try {
+ boolean mod = false;
+ boolean contentChanged = false;
+
+ if (subjectPatterns != null && subjectPatterns.length >
0) {
+ String subject = mail.getMessage().getSubject();
+ if (subject == null) subject = "";
+ subject = applyPatterns(subjectPatterns,
subjectSubstitutions, subjectFlags, subject, debug, this);
+ if (charset != null)
mail.getMessage().setSubject(subject, charset);
+ else mail.getMessage().setSubject(subject);
+ mod = true;
+ }
+
+ if (bodyPatterns != null && bodyPatterns.length > 0) {
+ Object bodyObj = mail.getMessage().getContent();
+ if (bodyObj == null) bodyObj = "";
+ if (bodyObj instanceof String) {
+ String body = (String) bodyObj;
+ body = applyPatterns(bodyPatterns,
bodySubstitutions, bodyFlags, body, debug, this);
+ String contentType =
mail.getMessage().getContentType();
+ if (charset != null) {
+ ContentType ct = new
ContentType(contentType);
+ ct.setParameter("charset",
charset);
+ contentType = ct.toString();
+ }
+ mail.getMessage().setContent(body,
contentType);
+ mod = true;
+ contentChanged = true;
+ }
+ }
+
+ if (charset != null && !contentChanged) {
+ ContentType ct = new
ContentType(mail.getMessage().getContentType());
+ ct.setParameter("charset", charset);
+ String contentType =
mail.getMessage().getContentType();
+
mail.getMessage().setContent(mail.getMessage().getContent(), contentType);
+ }
+
+ if (mod) mail.getMessage().saveChanges();
+
+ } catch (MessagingException e) {
+ throw new MailetException("Error in replace", e);
+
+ } catch (IOException e) {
+ throw new MailetException("Error in replace", e);
+ }
+ }
+
+}
Propchange:
james/server/trunk/src/java/org/apache/james/transport/mailets/ReplaceContent.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java?view=auto&rev=442021
==============================================================================
---
james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java
(added)
+++
james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,147 @@
+package org.apache.james.transport.mailets;
+
+import org.apache.james.util.mailet.FlowedMessageUtils;
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetException;
+
+import javax.mail.MessagingException;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Remove (best effort to) the hardcoded wrapping from a message.
+ * <p>If the text is "format=flowed" then deflows the text.
+ * <p>Otherwise it forces a dewrap of the text.
+ * <p>Parameters: quotewidth - when we try to dewrap e quoted text it helps
knowing the original
+ * with, so we can reconstruct "wrapped wraps" created by multiple wrappings
by clients with
+ * different original width or simply to the add of the heading ">" that
increase the line
+ * length.
+ * The value should be "WIDTH+X" if the original length is known, "-X"
otherwise.
+ * In the latter case the length of the longer line will be used.
+ * X is the tollerance needed for the quoting chars: if the original width is
known the suggested
+ * value for X is 2 (because of "> " prefix), otherwise it is suggested to
increase it to a value
+ * like 10 (-10)
+ *
+ * In summary, if the original wrap is known (for example 76, for flowed
messages)
+ * quotewidth = 78
+ * Otherwise
+ * quotewidth = -10
+ */
+public class UnwrapText extends GenericMailet {
+ public final static String PARAMETER_NAME_QUOTEWIDTH = "quotewidth";
+
+ private int quotewidth;
+
+ /**
+ * returns a String describing this mailet.
+ *
+ * @return A desciption of this mailet
+ */
+ public String getMailetInfo() {
+ return "UnwrapText";
+ }
+
+ public void init() throws MailetException {
+ quotewidth =
Integer.parseInt(getInitParameter(PARAMETER_NAME_QUOTEWIDTH, "-10"));
+ }
+
+ public void service(Mail mail) throws MailetException {
+ try {
+ // TODO replace non standard quotes (at least "> " with
">", otherwise the widely used "> > >" will not work.
+
+ if
(FlowedMessageUtils.isFlowedTextMessage(mail.getMessage()))
+
FlowedMessageUtils.deflowMessage(mail.getMessage());
+
+ else {
+ Object o = mail.getMessage().getContent();
+ if (o instanceof String) {
+ String unwrapped = unwrap((String) o,
quotewidth);
+ mail.getMessage().setContent(unwrapped,
mail.getMessage().getContentType());
+ mail.getMessage().saveChanges();
+ }
+ }
+
+ } catch (MessagingException e) {
+ throw new MailetException("Could not unwrap message",
e);
+
+ } catch (IOException e) {
+ throw new MailetException("Could not unwrap message",
e);
+ }
+
+ }
+
+ public static String unwrap(String text) {
+ return unwrap(text, - 10);
+ }
+
+ public static String unwrap(String text, int qwidth) {
+ String[] lines = text.split("\r\n|\n", -1);
+
+ //P1: Manage spaces without trims
+ Pattern p1 = Pattern.compile("([> ]*)(.*[^ .?!][ ]*)$", 0);
+
+ //P2: Quotation char at the begin of a line and the first word
starts with a lowercase char or a number. The word ends with a space, a tab or
a lineend.
+ Pattern p2 = Pattern.compile("^([> ]*)(([a-zèéàùìò][^
\t\r\n]*|[0-9][0-9,.]*)([ \t].*$|$))", 0);
+
+ // Width computation
+ int width = 0;
+ for (int i = 0; i < lines.length - 1; i++) {
+ String l = lines[i].trim();
+ if (l.length() > width) width = l.length();
+ }
+
+ if (width < 40) return text;
+ if (qwidth < 0) qwidth = width - qwidth;
+
+ StringBuffer result = new StringBuffer();
+ int prevWrapped = 0;
+ for (int i = 0; i < lines.length; i++) {
+ if (prevWrapped != 0) {
+ if (prevWrapped > 0 ) {
+ if (result.charAt(result.length() - 1)
!= ' ') result.append(" ");
+ }
+ else result.append("\r\n");
+ }
+ String l = lines[i];
+ Matcher m1 = p1.matcher(l);
+ Matcher m2 = i < lines.length - 1 ? p2.matcher(lines[i
+ 1]) : null;
+ boolean b;
+ int w;
+ // if patterns match, the quote level are identical and
if the line length added to the length of the following word is greater than
width then it is a wrapped line.
+ if (m1.matches() && i < lines.length - 1 &&
m2.matches() && (
+ // The following line has the same
quoting of the previous.
+ ((b =
m1.group(1).trim().equals(m2.group(1).trim())) && l.length() +
m2.group(3).length() + 1 > width)
+ ||
+ // The following line has no quoting
(while the previous yes)
+ (!b && m2.group(1).trim().equals("") &&
(w = l.length() + m2.group(2).trim().length() + 1) > width && w <= qwidth)
+ )) {
+
+ if (b) {
+ if (prevWrapped > 0 && m1.groupCount()
>= 2) result.append(m1.group(2));
+ else result.append(l);
+ prevWrapped = 1;
+
+ } else {
+ lines[i + 1] = l + (l.charAt(l.length()
- 1) != ' ' ? " " : "") + m2.group(2).trim();
+ // Revert the previous append
+ if (prevWrapped != 0) {
+ if (prevWrapped > 0)
result.deleteCharAt(result.length() - 1);
+ else
result.delete(result.length() - 2, result.length());
+ }
+ }
+
+ } else {
+ Matcher m3 = p2.matcher(l);
+ if (prevWrapped > 0 && m3.matches())
result.append(m3.group(2));
+ else result.append(lines[i]);
+ prevWrapped = -1;
+ }
+ }
+
+ return result.toString();
+ }
+
+}
Propchange:
james/server/trunk/src/java/org/apache/james/transport/mailets/UnwrapText.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java?view=auto&rev=442021
==============================================================================
---
james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java
(added)
+++
james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,56 @@
+package org.apache.james.transport.mailets;
+
+import org.apache.james.util.mailet.FlowedMessageUtils;
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetException;
+
+import javax.mail.MessagingException;
+
+import java.io.IOException;
+
+/**
+ * Convert a message to format=flowed
+ */
+public class WrapText extends GenericMailet {
+ private static final String PARAMETER_NAME_FLOWED_DELSP = "delsp";
+ private static final String PARAMETER_NAME_WIDTH = "width";
+
+ private boolean optionFlowedDelsp = false;
+ private int optionWidth = FlowedMessageUtils.RFC2646_WIDTH;
+
+ /**
+ * returns a String describing this mailet.
+ *
+ * @return A desciption of this mailet
+ */
+ public String getMailetInfo() {
+ return "WrapText";
+ }
+
+ private static boolean getBooleanParameter(String v, boolean def) {
+ return def ?
+ !(v != null && (v.equalsIgnoreCase("false") ||
v.equalsIgnoreCase("no"))) :
+ v != null &&
(v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes")) ;
+ }
+
+ public void init() throws MailetException {
+ optionFlowedDelsp =
getBooleanParameter(getInitParameter(PARAMETER_NAME_FLOWED_DELSP),
optionFlowedDelsp);
+ optionWidth =
Integer.parseInt(getInitParameter(PARAMETER_NAME_WIDTH, "" + optionWidth));
+ }
+
+ public void service(Mail mail) throws MailetException {
+ // TODO We could even manage the flow when the message is
quoted-printable
+
+ try {
+ FlowedMessageUtils.flowMessage(mail.getMessage(),
optionFlowedDelsp, optionWidth);
+
+ } catch (MessagingException e) {
+ throw new MailetException("Could not wrap message", e);
+
+ } catch (IOException e) {
+ throw new MailetException("Could not wrap message", e);
+ }
+
+ }
+}
Propchange:
james/server/trunk/src/java/org/apache/james/transport/mailets/WrapText.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java?view=auto&rev=442021
==============================================================================
---
james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java
(added)
+++
james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,281 @@
+package org.apache.james.util.mailet;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.ContentType;
+
+import java.io.IOException;
+
+/**
+ * Manage texts encoded as "text/plain; format=flowed"
+ * As a reference see RFC2646 and RFC3676 (new method with DelSP support).
+ * Url:
+ * http://www.rfc-editor.org/rfc/rfc2646.txt (or
http://www.zvon.org/tmRFC/RFC2646/Output/index.html )
+ * http://www.rfc-editor.org/rfc/rfc3676.txt (or
http://www.zvon.org/tmRFC/RFC3676/Output/index.html )
+ *
+ * IMPORTANT NOTES:
+ * - in order to decode, the input text must belong to a mail with headers
similar to:
+ * Content-Type: text/plain; charset="CHARSET"; [delsp="yes|no";
]format="flowed"
+ * (the quotes around CHARSET are not mandatory)
+ * Furthermore the header Content-Transfer-Encoding MUST NOT BE
Quoted-Printable
+ * (see RFC3676 paragraph 4.2).(In fact this happens often for non 7bit
messages).
+ * - when encoding the input text will be changed eliminating every space
found before CRLF,
+ * otherwise it won't be possible to recognize hard breaks from soft breaks.
+ * In this scenario encoding and decoding a message will not return a
message identical to
+ * the original (lines with hard breaks will be trimmed)
+ */
+public final class FlowedMessageUtils {
+ public static final char RFC2646_SPACE = ' ';
+ public static final char RFC2646_QUOTE = '>';
+ public static final String RFC2646_SIGNATURE = "-- ";
+ public static final String RFC2646_CRLF = "\r\n";
+ public static final String RFC2646_FROM = "From ";
+ public static final int RFC2646_WIDTH = 78;
+
+ private FlowedMessageUtils() {
+ // this class cannot be instantiated
+ }
+
+ /**
+ * Decodes a text previously wrapped using "format=flowed"
+ */
+ public static String deflow(String text, boolean delSp) {
+ String[] lines = text.split("\r\n|\n", -1);
+ StringBuffer result = null;
+ StringBuffer resultLine = new StringBuffer();
+ int resultLineQuoteDepth = 0;
+ boolean resultLineFlowed = false;
+ // One more cycle, to close the last line
+ for (int i = 0; i <= lines.length; i++) {
+ String line = i < lines.length ? lines[i] : null;
+ int actualQuoteDepth = 0;
+
+ if (line != null && line.length() > 0) {
+ if (line.equals(RFC2646_SIGNATURE))
+ // signature handling (the previous
line is not flowed)
+ resultLineFlowed = false;
+
+ else if (line.charAt(0) == RFC2646_QUOTE) {
+ // Quote
+ actualQuoteDepth = 1;
+ while (actualQuoteDepth < line.length()
&& line.charAt(actualQuoteDepth) == RFC2646_QUOTE) actualQuoteDepth ++;
+ // if quote-depth changes wrt the
previous line then this is not flowed
+ if (resultLineQuoteDepth !=
actualQuoteDepth) resultLineFlowed = false;
+ line = line.substring(actualQuoteDepth);
+
+ } else {
+ // id quote-depth changes wrt the first
line then this is not flowed
+ if (resultLineQuoteDepth > 0)
resultLineFlowed = false;
+ }
+
+ if (line.length() > 0 && line.charAt(0) ==
RFC2646_SPACE)
+ // Line space-stuffed
+ line = line.substring(1);
+
+ // if the previous was the last then it was not flowed
+ } else if (line == null) resultLineFlowed = false;
+
+ // Add the PREVIOUS line.
+ // This often will find the flow looking for a space
as the last char of the line.
+ // With quote changes or signatures it could be the
followinf line to void the flow.
+ if (!resultLineFlowed && i > 0) {
+ if (resultLineQuoteDepth > 0)
resultLine.insert(0, RFC2646_SPACE);
+ for (int j = 0; j < resultLineQuoteDepth; j++)
resultLine.insert(0, RFC2646_QUOTE);
+ if (result == null) result = new StringBuffer();
+ else result.append(RFC2646_CRLF);
+ result.append(resultLine.toString());
+ resultLine = new StringBuffer();
+ resultLineFlowed = false;
+ }
+ resultLineQuoteDepth = actualQuoteDepth;
+
+ if (line != null) {
+ if (!line.equals(RFC2646_SIGNATURE) &&
line.endsWith("" + RFC2646_SPACE) && i < lines.length - 1) {
+ // Line flowed (NOTE: for the split
operation the line having i == lines.length is the last that does not end with
RFC2646_CRLF)
+ if (delSp) line = line.substring(0,
line.length() - 1);
+ resultLineFlowed = true;
+ }
+
+ else resultLineFlowed = false;
+
+ resultLine.append(line);
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Obtains the content of the encoded message, if previously encoded as
format=flowed.
+ */
+ public static String deflow(Message m) throws IOException,
MessagingException {
+ ContentType ct = new ContentType(m.getContentType());
+ String format = ct.getParameter("format");
+ if (ct.getBaseType().equals("text/plain") && format != null &&
format.equalsIgnoreCase("flowed")) {
+ String delSp = ct.getParameter("delsp");
+ return deflow((String) m.getContent(), delSp != null &&
delSp.equalsIgnoreCase("yes"));
+
+ } else if (ct.getPrimaryType().equals("text")) return (String)
m.getContent();
+
+ else return null;
+ }
+
+ /**
+ * If the message is formad=flowed set the encoded version as message
content
+ */
+ public static void deflowMessage(Message m) throws MessagingException,
IOException {
+ ContentType ct = new ContentType(m.getContentType());
+ String format = ct.getParameter("format");
+ if (ct.getBaseType().equals("text/plain") && format != null &&
format.equalsIgnoreCase("flowed")) {
+ String delSp = ct.getParameter("delsp");
+ String deflowed = deflow((String) m.getContent(), delSp
!= null && delSp.equalsIgnoreCase("yes"));
+
+ ct.getParameterList().remove("format");
+ ct.getParameterList().remove("delsp");
+
+ if (ct.toString().indexOf("flowed") >= 0)
+
System.out.println("\n\n*************************\n* ERROR!!!
FlowedMessageUtils dind't remove the flowed correctly!\n******************\n\n"
+ ct.toString() + " \n " + ct.toString() + "\n");
+
+ m.setContent(deflowed, ct.toString());
+ m.saveChanges();
+ }
+ }
+
+
+ /**
+ * Encode a text (using standard with)
+ */
+ public static String flow(String text, boolean delSp) {
+ return flow(text, delSp, RFC2646_WIDTH);
+ }
+
+ /**
+ * Decode a text
+ */
+ public static String flow(String text, boolean delSp, int width) {
+ StringBuffer result = new StringBuffer();
+ String[] lines = text.split("\r\n|\n", -1);
+ for (int i = 0; i < lines.length; i ++) {
+ String line = lines[i];
+ boolean notempty = line.length() > 0;
+
+ int quoteDepth = 0;
+ while (quoteDepth < line.length() &&
line.charAt(quoteDepth) == RFC2646_QUOTE) quoteDepth ++;
+ if (quoteDepth > 0) {
+ if (quoteDepth + 1 < line.length() &&
line.charAt(quoteDepth) == RFC2646_SPACE) line = line.substring(quoteDepth + 1);
+ else line = line.substring(quoteDepth);
+ }
+
+ while (notempty) {
+ int extra = 0;
+ if (quoteDepth == 0) {
+ if (line.startsWith("" + RFC2646_SPACE)
|| line.startsWith("" + RFC2646_QUOTE) || line.startsWith(RFC2646_FROM)) {
+ line = "" + RFC2646_SPACE +
line;
+ extra = 1;
+ }
+ } else {
+ line = RFC2646_SPACE + line;
+ for (int j = 0; j < quoteDepth; j++)
line = "" + RFC2646_QUOTE + line;
+ extra = quoteDepth + 1;
+ }
+
+ int j = width - 1;
+ if (j >= line.length()) j = line.length() - 1;
+ else {
+ while (j >= extra && ((delSp &&
isAlphaChar(text, j)) || (!delSp && line.charAt(j) != RFC2646_SPACE))) j --;
+ if (j < extra) {
+ // Not able to cut a word: skip
to word end even if greater than the max width
+ j = width - 1;
+ while (j < line.length() - 1 &&
((delSp && isAlphaChar(text, j)) || (!delSp && line.charAt(j) !=
RFC2646_SPACE))) j ++;
+ }
+ }
+
+ result.append(line.substring(0, j + 1));
+ if (j < line.length() - 1) {
+ if (delSp) result.append(RFC2646_SPACE);
+ result.append(RFC2646_CRLF);
+ }
+
+ line = line.substring(j + 1);
+ notempty = line.length() > 0;
+ }
+
+ if (i < lines.length - 1) {
+ // NOTE: Have to trim the spaces before,
otherwise it won't recognize soft-break from hard break.
+ // Deflow of flowed message will not be
identical to the original.
+ while (result.length() > 0 &&
result.charAt(result.length() - 1) == RFC2646_SPACE)
result.deleteCharAt(result.length() - 1);
+ result.append(RFC2646_CRLF);
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Encode the input text and sets it as the new message content
+ */
+ public static void setFlowedContent(Message m, String text, boolean
delSp) throws MessagingException {
+ setFlowedContent(m, text, delSp, RFC2646_WIDTH, true, null);
+ }
+
+ /**
+ * Encode the input text and sets it as the new message content
+ */
+ public static void setFlowedContent(Message m, String text, boolean
delSp, int width, boolean preserveCharset, String charset) throws
MessagingException {
+ String coded = flow(text, delSp, width);
+ if (preserveCharset) {
+ ContentType ct = new ContentType(m.getContentType());
+ charset = ct.getParameter("charset");
+ }
+ ContentType ct = new ContentType();
+ ct.setPrimaryType("text");
+ ct.setSubType("plain");
+ if (charset != null) ct.setParameter("charset", charset);
+ ct.setParameter("format", "flowed");
+ if (delSp) ct.setParameter("delsp", "yes");
+ m.setContent(coded, ct.toString());
+ m.saveChanges();
+ }
+
+ /**
+ * Encode the message content (if text/plain).
+ */
+ public static void flowMessage(Message m, boolean delSp) throws
MessagingException, IOException {
+ flowMessage(m, delSp, RFC2646_WIDTH);
+ }
+
+ /**
+ * Encode the message content (if text/plain).
+ */
+ public static void flowMessage(Message m, boolean delSp, int width)
throws MessagingException, IOException {
+ ContentType ct = new ContentType(m.getContentType());
+ if (!ct.getBaseType().equals("text/plain")) return;
+ String format = ct.getParameter("format");
+ String text = format != null && format.equals("flowed") ?
deflow(m) : (String) m.getContent();
+ String coded = flow(text, delSp, width);
+ ct.setParameter("format", "flowed");
+ if (delSp) ct.setParameter("delsp", "yes");
+ m.setContent(coded, ct.toString());
+ m.saveChanges();
+ }
+
+ /**
+ * Check wether the char is part of a word
+ * <p>RFC assert a word cannot be splitted (even if the length is
greater than the maximum length)
+ */
+ public static boolean isAlphaChar(String text, int index) {
+ // Note: a list of chars is available here:
+ // http://www.zvon.org/tmRFC/RFC2646/Output/index.html
+ char c = text.charAt(index);
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c
>= '0' && c <= '9');
+ }
+
+ /**
+ * Check wether the input message is formad=flowed
+ */
+ public static boolean isFlowedTextMessage(Message m) throws
MessagingException {
+ ContentType ct = new ContentType(m.getContentType());
+ String format = ct.getParameter("format");
+ return ct.getBaseType().equals("text/plain") && format != null
&& format.equalsIgnoreCase("flowed");
+ }
+}
Propchange:
james/server/trunk/src/java/org/apache/james/util/mailet/FlowedMessageUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java?view=auto&rev=442021
==============================================================================
--- james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java
(added)
+++ james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java
Sun Sep 10 15:35:06 2006
@@ -0,0 +1,75 @@
+package org.apache.james.util.mailet;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+
+public final class StringUtils {
+
+ private StringUtils() {
+ // make this class non instantiable
+ }
+
+ /**
+ * Split a string given a pattern (regex), considering escapes
+ * <p> E.g: considering a pattern "," we have:
+ * one,two,three => {one},{two},{three}
+ * one\,two\\,three => {one,two\\},{three}
+ *
+ * NOTE: Untested with pattern regex as pattern and untested for escape
chars in text or pattern.
+ */
+ public static String[] split(String text, String pattern) {
+ String[] array = text.split(pattern, -1);
+ ArrayList list = new ArrayList();
+ for (int i = 0; i < array.length; i++) {
+ boolean escaped = false;
+ if (i > 0 && array[i - 1].endsWith("\\")) {
+ // When the number of trailing "\" is odd then
there was no separator and this pattern is part of
+ // the previous match.
+ int depth = 1;
+ while (depth < array[i-1].length() &&
array[i-1].charAt(array[i-1].length() - 1 - depth) == '\\') depth ++;
+ escaped = depth % 2 == 1;
+ }
+ if (!escaped) list.add(array[i]);
+ else {
+ String prev = (String) list.remove(list.size()
- 1);
+ list.add(prev.substring(0, prev.length() - 1) +
pattern + array[i]);
+ }
+ }
+ return (String[]) list.toArray(new String[0]);
+ }
+
+ public static String md5(java.lang.String message) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ StringBuffer sb = new StringBuffer();
+ byte buf[] = message.getBytes();
+ byte[] md5 = md.digest(buf);
+ //System.out.println(message);
+ for (int i = 0; i < md5.length; i++) {
+ String tmpStr = "0" + Integer.toHexString((0xff
& md5[i]));
+ sb.append(tmpStr.substring(tmpStr.length() -
2));
+ }
+ return sb.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+
+ public static String capitalizeWords(String data) {
+ if (data==null) return null;
+ StringBuffer res = new StringBuffer();
+ char ch;
+ char prevCh = '.';
+ for ( int i = 0; i < data.length(); i++ ) {
+ ch = data.charAt(i);
+ if ( Character.isLetter(ch)) {
+ if (!Character.isLetter(prevCh) ) res.append(
Character.toUpperCase(ch) );
+ else res.append( Character.toLowerCase(ch) );
+ } else res.append( ch );
+ prevCh = ch;
+ }
+ return res.toString();
+ }
+}
Propchange:
james/server/trunk/src/java/org/apache/james/util/mailet/StringUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]