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 &lt;subjectPattern&gt; 
and &lt;bodyPattern&gt;
+ * 
+ * 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 &lt;subjectPatternFile&gt; and 
+ * &lt;bodyPatternFile&gt;
+ * 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 &lt;charset&gt; 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 &lt;subjectPattern&gt; or 
&lt;bodyPattern&gt; 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]

Reply via email to