vincenzo 2004/07/16 06:46:16
Modified: src/java/org/apache/james/transport/matchers
AttachmentFileNameIs.java
Log:
Added:
1) Fixed JIRA JAMES-303 "AttachmentFileNameIs matcher should be able to analyze
zip file attachments"
2) Fixed JIRA JAMES-305 "AttachmentFileNameIs never matches if the message is
S/MIME signed". Does recursion, looking for embedded multiparts.
3) Fixed JIRA JAMES-306 "AttachmentFileNameIs may throw
UnsupportedEncodingException".
a) Such exceptions are totally harmless and as such are now completely ignored.
b) Other exceptions are thrown up (to be handled by "onMatchException" clause)
and now logged if debug is requested ("-d" parameter).
To analyze zip files code
... match="AttachmentFileNameIs=-z ...";
to debug code
... match="AttachmentFileNameIs=-d ...".
Revision Changes Path
1.7 +154 -54
james-server/src/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java
Index: AttachmentFileNameIs.java
===================================================================
RCS file:
/home/cvs/james-server/src/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- AttachmentFileNameIs.java 30 Jan 2004 02:22:13 -0000 1.6
+++ AttachmentFileNameIs.java 16 Jul 2004 13:46:16 -0000 1.7
@@ -14,37 +14,55 @@
* implied. See the License for the specific language governing *
* permissions and limitations under the License. *
***********************************************************************/
-
package org.apache.james.transport.matchers;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Locale;
-import java.util.StringTokenizer;
+import org.apache.mailet.GenericMatcher;
+import org.apache.mailet.Mail;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.MimeMessage;
-
-import org.apache.mailet.GenericMatcher;
-import org.apache.mailet.Mail;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Locale;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipEntry;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
/**
* <P>Checks if at least one attachment has a file name which matches any
- * element of a comma-separated list of file name masks.
- * The match is case insensitive.</P>
+ * element of a comma-separated or space-separated list of file name masks.</P>
+ * <P>Syntax: <CODE>match="AttachmentFileNameIs=[-d] [-z] <I>masks</I>"</CODE></P>
+ * <P>The match is case insensitive.</P>
* <P>File name masks may start with a wildcard '*'.</P>
* <P>Multiple file name masks can be specified, e.g.: '*.scr,*.bat'.</P>
+ * <P>If '<CODE>-d</CODE>' is coded, some debug info will be logged.</P>
+ * <P>If '<CODE>-z</CODE>' is coded, the check will be non-recursively applied
+ * to the contents of any attached '*.zip' file.</P>
*
* @version CVS $Revision$ $Date$
* @since 2.2.0
*/
public class AttachmentFileNameIs extends GenericMatcher {
+
+ /** Unzip request parameter. */
+ protected static final String UNZIP_REQUEST_PARAMETER = "-z";
+
+ /** Debug request parameter. */
+ protected static final String DEBUG_REQUEST_PARAMETER = "-d";
+
+ /** Match string for zip files. */
+ protected static final String ZIP_SUFFIX = ".zip";
+
/**
- * represents a single parsed file name mask
+ * represents a single parsed file name mask.
*/
private static class Mask {
/** true if the mask starts with a wildcard asterisk */
@@ -54,18 +72,37 @@
public String matchString;
}
+ /**
+ * Controls certain log messages.
+ */
+ protected boolean isDebug = false;
+
/** contains ParsedMask instances, setup by init */
private Mask[] masks = null;
- /** parses the condition */
+ /** True if unzip is requested. */
+ protected boolean unzipIsRequested;
+
public void init() throws MessagingException {
- /** sets up fileNameMasks variable by parsing the condition */
+ /* sets up fileNameMasks variable by parsing the condition */
StringTokenizer st = new StringTokenizer(getCondition(), ", ", false);
ArrayList theMasks = new ArrayList(20);
while (st.hasMoreTokens()) {
- Mask mask = new Mask();
String fileName = st.nextToken();
+
+ // check possible parameters at the beginning of the condition
+ if (theMasks.size() == 0 &&
fileName.equalsIgnoreCase(UNZIP_REQUEST_PARAMETER)) {
+ unzipIsRequested = true;
+ log("zip file analysis requested");
+ continue;
+ }
+ if (theMasks.size() == 0 &&
fileName.equalsIgnoreCase(DEBUG_REQUEST_PARAMETER)) {
+ isDebug = true;
+ log("debug requested");
+ continue;
+ }
+ Mask mask = new Mask();
if (fileName.startsWith("*")) {
mask.suffixMatch = true;
mask.matchString = fileName.substring(1);
@@ -73,8 +110,7 @@
mask.suffixMatch = false;
mask.matchString = fileName;
}
- mask.matchString = mask.matchString.toLowerCase(Locale.US);
- mask.matchString = mask.matchString.trim();
+ mask.matchString = cleanFileName(mask.matchString);
theMasks.add(mask);
}
masks = (Mask[])theMasks.toArray(new Mask[0]);
@@ -86,59 +122,88 @@
*/
public Collection match(Mail mail) throws MessagingException {
- Exception anException = null;
-
try {
MimeMessage message = mail.getMessage();
- Object content;
- /**
- * if there is an attachment and no inline text,
- * the content type can be anything
- */
- if (message.getContentType() == null) {
- return null;
+ if (matchFound(message)) {
+ return mail.getRecipients(); // matching file found
+ } else {
+ return null; // no matching attachment found
}
- content = message.getContent();
- if (content instanceof Multipart) {
- Multipart multipart = (Multipart) content;
- for (int i = 0; i < multipart.getCount(); i++) {
- try {
- Part part = multipart.getBodyPart(i);
- String fileName = part.getFileName();
- if (fileName != null && matchFound(fileName)) {
- return mail.getRecipients(); // matching file found
- }
- } catch (MessagingException e) {
- anException = e;
- } // ignore any messaging exception and process next bodypart
+ } catch (Exception e) {
+ if (isDebug) {
+ log("Malformed message", e);
+ }
+ throw new MessagingException("Malformed message", e);
+ }
+ }
+
+ /**
+ * Checks if <I>part</I> matches with at least one of the <CODE>masks</CODE>.
+ */
+ protected boolean matchFound(Part part) throws Exception {
+
+ /*
+ * if there is an attachment and no inline text,
+ * the content type can be anything
+ */
+ if (part.getContentType() == null) {
+ return false;
+ }
+
+ Object content;
+
+ try {
+ content = part.getContent();
+ } catch (UnsupportedEncodingException uee) {
+ // in this case it is not an attachment, so ignore it
+ return false;
+ }
+
+ Exception anException = null;
+
+ if (content instanceof Multipart) {
+ Multipart multipart = (Multipart) content;
+ for (int i = 0; i < multipart.getCount(); i++) {
+ try {
+ Part bodyPart = multipart.getBodyPart(i);
+ if (matchFound(bodyPart)) {
+ return true; // matching file found
+ }
+ } catch (MessagingException e) {
+ anException = e;
+ } // remember any messaging exception and process next bodypart
+ }
+ } else {
+ String fileName = part.getFileName();
+ if (fileName != null) {
+ fileName = cleanFileName(fileName);
+ // check the file name
+ if (matchFound(fileName)) {
+ if (isDebug) {
+ log("matched " + fileName);
+ }
+ return true;
}
- } else {
- String fileName = message.getFileName();
- if (fileName != null && matchFound(fileName)) {
- return mail.getRecipients(); // matching file found
+ if (unzipIsRequested && fileName.endsWith(ZIP_SUFFIX) &&
matchFoundInZip(part)){
+ return true;
}
}
- } catch (Exception e) {
- anException = e;
}
// if no matching attachment was found and at least one exception was
catched rethrow it up
if (anException != null) {
- throw new MessagingException("Malformed message", anException);
+ throw anException;
}
- return null; // no matching attachment found
+ return false;
}
-
- /*
+
+ /**
* Checks if <I>fileName</I> matches with at least one of the
<CODE>masks</CODE>.
*/
- private boolean matchFound(String fileName) {
- fileName = fileName.toLowerCase(Locale.US);
- fileName = fileName.trim();
-
+ protected boolean matchFound(String fileName) {
for (int j = 0; j < masks.length; j++) {
boolean fMatch;
Mask mask = masks[j];
@@ -149,9 +214,44 @@
} else {
fMatch = fileName.equals(mask.matchString);
}
- if (fMatch) return true; // matching file found
+ if (fMatch) {
+ return true; // matching file found
+ }
}
return false;
+ }
+
+ /**
+ * Checks if <I>part</I> is a zip containing a file that matches with at least
one of the <CODE>masks</CODE>.
+ */
+ protected boolean matchFoundInZip(Part part) throws MessagingException,
IOException {
+ ZipInputStream zis = new ZipInputStream(part.getInputStream());
+
+ try {
+ while (true) {
+ ZipEntry zipEntry = zis.getNextEntry();
+ if (zipEntry == null) {
+ break;
+ }
+ String fileName = zipEntry.getName();
+ if (matchFound(fileName)) {
+ if (isDebug) {
+ log("matched " + part.getFileName() + "(" + fileName + ")");
+ }
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ zis.close();
+ }
+ }
+
+ /**
+ * Transforms <I>fileName<I> in a trimmed lowercase string usable for matching
agains the masks.
+ */
+ protected String cleanFileName(String fileName) {
+ return fileName.toLowerCase(Locale.US).trim();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]