001: /*
002: *
003: * Copyright (C) 2005 Pierre-Alexandre Losson, plosson@users.sourceforge.net
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * as published by the Free Software Foundation; either version 2
008: * of the License, or (at your option) any later version.
009: *
010: * This program is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU General Public License for more details.
014: *
015: * You should have received a copy of the GNU General Public License
016: * along with this program; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: *
019: */
020: package org.jsmtpd.plugins.filters.snippets;
021:
022: import java.io.BufferedReader;
023: import java.io.ByteArrayInputStream;
024: import java.io.ByteArrayOutputStream;
025: import java.io.File;
026: import java.io.FileReader;
027: import java.io.IOException;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.Set;
031: import java.util.regex.Matcher;
032: import java.util.regex.Pattern;
033:
034: import javax.mail.MessagingException;
035: import javax.mail.internet.MimeBodyPart;
036: import javax.mail.internet.MimeMessage;
037: import javax.mail.internet.MimeMultipart;
038: import javax.mail.internet.MimePart;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.jsmtpd.core.common.PluginInitException;
043: import org.jsmtpd.core.common.filter.FilterTreeFailureException;
044: import org.jsmtpd.core.common.filter.FilterTreeSuccesException;
045: import org.jsmtpd.core.common.filter.IFilter;
046: import org.jsmtpd.core.mail.Email;
047: import org.jsmtpd.tools.ByteArrayTool;
048:
049: /**
050: * This filter adds a signature to mail bodies
051: *
052: * @author Pierre-Alexandre Losson, plosson@users.sourceforge.net
053: *
054: * jfp : format to jsmtpd coding style, added logs, relative path in patterns fix.
055: *
056: *
057: *
058: */
059: public class ReplaceSnippetFilter implements IFilter {
060:
061: protected static final int BUFFER_SIZE = 5 * 1024; // 5k
062: protected static final String CR_LF = "\r\n";
063: protected static final String SNIPPET_RE = "[A-Za-z0-9]+";
064: private Log log = LogFactory.getLog(ReplaceSnippetFilter.class);
065:
066: private String suffix;
067: private String prefix;
068: private String path;
069: private Pattern pattern;
070: private String encoding = "utf-8";
071:
072: public ReplaceSnippetFilter() {
073: }
074:
075: public void setPath(String path) {
076: this .path = path;
077: }
078:
079: public void setPrefix(String prefix) {
080: this .prefix = prefix;
081: }
082:
083: public void setSuffix(String suffix) {
084: this .suffix = suffix;
085: }
086:
087: public void initPlugin() throws PluginInitException {
088: pattern = Pattern.compile(prefix + "(" + SNIPPET_RE + ")"
089: + suffix, Pattern.MULTILINE);
090: log.debug("Filter initialised with pattern "
091: + pattern.pattern());
092: }
093:
094: public boolean doFilter(Email input)
095: throws FilterTreeFailureException,
096: FilterTreeSuccesException {
097: try {
098: MimeMessage part = new MimeMessage(null,
099: new ByteArrayInputStream(input.getDataAsByte()));
100: replaceSnippets(input.getFrom().getUser(), part);
101: part.saveChanges();
102: ByteArrayOutputStream bos = new ByteArrayOutputStream();
103: part.writeTo(bos);
104: input.setDataBuffer(ByteArrayTool
105: .crlfFix(bos.toByteArray()));
106: } catch (Exception e) {
107: log.error("An error occured", e);
108: }
109: return true;
110: }
111:
112: private boolean replaceSnippets(String user, MimePart part)
113: throws MessagingException, IOException {
114: if (part.isMimeType("text/plain")) {
115: addToText(user, part);
116: return true;
117: } else if (part.isMimeType("text/html")) {
118: addToHTML(user, part);
119: return true;
120: } else if (part.isMimeType("multipart/mixed")) {
121: MimeMultipart multipart = (MimeMultipart) part.getContent();
122: MimeBodyPart firstPart = (MimeBodyPart) multipart
123: .getBodyPart(0);
124: boolean isFooterAttached = replaceSnippets(user, firstPart);
125: //We have to do this because of a bug in JavaMail (ref id 4404733)
126: part.setContent(multipart);
127: return isFooterAttached;
128: } else if (part.isMimeType("multipart/alternative")) {
129: MimeMultipart multipart = (MimeMultipart) part.getContent();
130: int count = multipart.getCount();
131: boolean isFooterAttached = false;
132: for (int index = 0; index < count; index++) {
133: MimeBodyPart mimeBodyPart = (MimeBodyPart) multipart
134: .getBodyPart(index);
135: isFooterAttached |= replaceSnippets(user, mimeBodyPart);
136: }
137: //We have to do this because of a bug in JavaMail (ref id 4404733)
138: part.setContent(multipart);
139: return isFooterAttached;
140: } else {
141: //Give up... we won't attach the footer to this MimePart
142: log.error("Can't attach signature to mail ");
143: return false;
144: }
145: }
146:
147: public String getPluginName() {
148: return "Replace Snippet Filter";
149: }
150:
151: public void shutdownPlugin() {
152: }
153:
154: /**
155: * Prepends the content of the MimePart as HTML to the existing footer
156: *
157: * @param part the MimePart to attach
158: * @throws javax.mail.MessagingException
159: * @throws java.io.IOException
160: */
161: protected void addToHTML(String user, MimePart part)
162: throws MessagingException, IOException {
163: String content = part.getContent().toString();
164: part.setContent(replaceSnippets(user, content, "text/html"),
165: part.getContentType());
166: }
167:
168: protected void addToText(String user, MimePart part)
169: throws MessagingException, IOException {
170: String content = part.getContent().toString();
171: part.setText(replaceSnippets(user, content, "text/plain"),
172: encoding);
173: }
174:
175: protected String replaceSnippets(String user, String content,
176: String contentType) {
177: Matcher m = pattern.matcher(content);
178: Set<String> snippets = new HashSet<String>();
179: while (m.find()) {
180: String match = m.group(1);
181: if (hasSnippet(user, match, contentType)) {
182: snippets.add(match);
183: }
184: }
185:
186: Iterator<String> it = snippets.iterator();
187: String newContent = content;
188: while (it.hasNext()) {
189: String match = it.next();
190: String pattern = prefix + match + suffix;
191: String replace = getSnippet(user, match, contentType);
192: if (replace != null) {
193: newContent = Pattern
194: .compile(pattern, Pattern.MULTILINE).matcher(
195: newContent).replaceAll(replace);
196: } else {
197: log.error("Could not replace content for snippet"
198: + match + " in " + contentType + " for user "
199: + user);
200: return content;
201: }
202: }
203: return newContent;
204: }
205:
206: public boolean hasSnippet(String user, String pattern,
207: String contentType) {
208:
209: if (pattern.contains("../"))
210: return false;
211:
212: String snippetPath = getSnippetPath(user, pattern, contentType);
213: boolean result = new File(snippetPath).exists();
214: if (!result) {
215: log.debug("Signature " + snippetPath + " not found");
216: }
217: return result;
218: }
219:
220: public String getSnippet(String user, String pattern,
221: String contentType) {
222: StringBuffer content = new StringBuffer();
223: try {
224: BufferedReader in = new BufferedReader(new FileReader(
225: getSnippetPath(user, pattern, contentType)));
226: String str;
227: while ((str = in.readLine()) != null) {
228: content.append(str);
229: content.append(CR_LF);
230: }
231: in.close();
232: } catch (IOException e) {
233: log.error("Error getting snippet content", e);
234: return null;
235: }
236: return content.toString();
237: }
238:
239: private String getSnippetPath(String user, String pattern,
240: String contentType) {
241: String snippet = path + File.separator + user + File.separator
242: + pattern;
243: if (contentType.equals("text/html"))
244: snippet = snippet + ".html";
245: else
246: snippet = snippet + ".txt";
247: return snippet;
248: }
249:
250: public void setEncoding(String encoding) {
251: this.encoding = encoding;
252: }
253:
254: }
|