1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james.transport.mailets;
23
24 import org.apache.avalon.cornerstone.services.datasources.DataSourceSelector;
25 import org.apache.avalon.excalibur.datasource.DataSourceComponent;
26 import org.apache.avalon.framework.service.ServiceManager;
27 import org.apache.james.Constants;
28 import org.apache.james.api.user.JamesUser;
29 import org.apache.james.api.user.UsersRepository;
30 import org.apache.james.util.sql.JDBCUtil;
31 import org.apache.james.util.sql.SqlResources;
32 import org.apache.mailet.base.GenericMailet;
33 import org.apache.mailet.Mail;
34 import org.apache.mailet.MailAddress;
35 import org.apache.mailet.base.RFC2822Headers;
36 import org.apache.mailet.base.RFC822DateFormat;
37
38 import javax.mail.Message;
39 import javax.mail.MessagingException;
40 import javax.mail.Session;
41 import javax.mail.internet.InternetAddress;
42 import javax.mail.internet.MimeBodyPart;
43 import javax.mail.internet.MimeMessage;
44 import javax.mail.internet.MimeMultipart;
45
46 import java.io.File;
47 import java.io.IOException;
48 import java.io.PrintWriter;
49 import java.io.StringWriter;
50 import java.sql.Connection;
51 import java.sql.DatabaseMetaData;
52 import java.sql.PreparedStatement;
53 import java.sql.ResultSet;
54 import java.sql.SQLException;
55 import java.util.Collection;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import java.util.Locale;
60 import java.util.Map;
61 import java.util.Set;
62 import java.util.StringTokenizer;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public class WhiteListManager extends GenericMailet {
115
116 private boolean automaticInsert;
117 private String displayFlag;
118 private String insertFlag;
119 private String removeFlag;
120 private MailAddress whitelistManagerAddress;
121
122 private String selectByPK;
123 private String selectBySender;
124 private String insert;
125 private String deleteByPK;
126
127
128 private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
129
130 private DataSourceComponent datasource;
131
132
133
134
135 private UsersRepository localusers;
136
137
138
139
140 private final JDBCUtil theJDBCUtil = new JDBCUtil() {
141 protected void delegatedLog(String logString) {
142 log("WhiteListManager: " + logString);
143 }
144 };
145
146
147
148
149 private SqlResources sqlQueries = new SqlResources();
150
151
152
153
154 private File sqlFile;
155
156
157
158
159 private Map sqlParameters = new HashMap();
160
161
162
163
164
165 private Map getSqlParameters() {
166
167 return this.sqlParameters;
168 }
169
170
171
172 public void init() throws MessagingException {
173 automaticInsert = new Boolean(getInitParameter("automaticInsert")).booleanValue();
174 log("automaticInsert: " + automaticInsert);
175
176 displayFlag = getInitParameter("displayFlag");
177 insertFlag = getInitParameter("insertFlag");
178 removeFlag = getInitParameter("removeFlag");
179
180 String whitelistManagerAddressString = getInitParameter("whitelistManagerAddress");
181 if (whitelistManagerAddressString != null) {
182 whitelistManagerAddressString = whitelistManagerAddressString.trim();
183 log("whitelistManagerAddress: " + whitelistManagerAddressString);
184 try {
185 whitelistManagerAddress = new MailAddress(whitelistManagerAddressString);
186 }
187 catch (javax.mail.internet.ParseException pe) {
188 throw new MessagingException("Bad whitelistManagerAddress", pe);
189 }
190
191 if (displayFlag != null) {
192 displayFlag = displayFlag.trim();
193 log("displayFlag: " + displayFlag);
194 }
195 else {
196 log("displayFlag is null");
197 }
198 if (insertFlag != null) {
199 insertFlag = insertFlag.trim();
200 log("insertFlag: " + insertFlag);
201 }
202 else {
203 log("insertFlag is null");
204 }
205 if (removeFlag != null) {
206 removeFlag = removeFlag.trim();
207 log("removeFlag: " + removeFlag);
208 }
209 else {
210 log("removeFlag is null");
211 }
212 }
213 else {
214 log("whitelistManagerAddress is null; will ignore commands");
215 }
216
217 String repositoryPath = getInitParameter("repositoryPath");
218 if (repositoryPath != null) {
219 log("repositoryPath: " + repositoryPath);
220 }
221 else {
222 throw new MessagingException("repositoryPath is null");
223 }
224
225 ServiceManager serviceManager = (ServiceManager) getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
226
227 try {
228
229 DataSourceSelector datasources = (DataSourceSelector) serviceManager.lookup(DataSourceSelector.ROLE);
230
231 int stindex = repositoryPath.indexOf("://") + 3;
232 String datasourceName = repositoryPath.substring(stindex);
233 datasource = (DataSourceComponent) datasources.select(datasourceName);
234 } catch (Exception e) {
235 throw new MessagingException("Can't get datasource", e);
236 }
237
238 try {
239
240 localusers = (UsersRepository)serviceManager.lookup(UsersRepository.ROLE);
241 } catch (Exception e) {
242 throw new MessagingException("Can't get the local users repository", e);
243 }
244
245 try {
246 initSqlQueries(datasource.getConnection(), getMailetContext());
247 } catch (Exception e) {
248 throw new MessagingException("Exception initializing queries", e);
249 }
250
251 selectByPK = sqlQueries.getSqlString("selectByPK", true);
252 selectBySender = sqlQueries.getSqlString("selectBySender", true);
253 insert = sqlQueries.getSqlString("insert", true);
254 deleteByPK = sqlQueries.getSqlString("deleteByPK", true);
255 }
256
257
258
259 public void service(Mail mail) throws MessagingException {
260
261
262 MailAddress senderMailAddress = mail.getSender();
263 if (senderMailAddress == null) {
264 return;
265 }
266 if (!getMailetContext().isLocalEmail(senderMailAddress)) {
267
268 return;
269 }
270
271 Collection recipients = mail.getRecipients();
272
273 if (recipients.size() == 1
274 && whitelistManagerAddress != null
275 && whitelistManagerAddress.equals(recipients.toArray()[0])) {
276
277 mail.setState(Mail.GHOST);
278
279 String subject = mail.getMessage().getSubject();
280 if (displayFlag != null && displayFlag.equals(subject)) {
281 manageDisplayRequest(mail);
282 }
283 else if (insertFlag != null && insertFlag.equals(subject)) {
284 manageInsertRequest(mail);
285 }
286 else if (removeFlag != null && removeFlag.equals(subject)) {
287 manageRemoveRequest(mail);
288 }
289 else {
290 StringWriter sout = new StringWriter();
291 PrintWriter out = new PrintWriter(sout, true);
292 out.println("Answering on behalf of: " + whitelistManagerAddress);
293 out.println("ERROR: Unknown command in the subject line: " + subject);
294 sendReplyFromPostmaster(mail, sout.toString());
295 }
296 return;
297 }
298
299 if (automaticInsert) {
300 checkAndInsert(senderMailAddress, recipients);
301 }
302
303 }
304
305
306
307
308
309 public String getMailetInfo() {
310 return "White List Manager mailet";
311 }
312
313
314
315
316 private void checkAndInsert(MailAddress senderMailAddress, Collection recipients) throws MessagingException {
317 String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
318 String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
319
320 senderUser = getPrimaryName(senderUser);
321
322 Connection conn = null;
323 PreparedStatement selectStmt = null;
324 PreparedStatement insertStmt = null;
325 boolean dbUpdated = false;
326
327 try {
328
329 for (Iterator i = recipients.iterator(); i.hasNext(); ) {
330 ResultSet selectRS = null;
331 try {
332 MailAddress recipientMailAddress = (MailAddress)i.next();
333 String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
334 String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
335
336 if (getMailetContext().isLocalServer(recipientHost)) {
337
338 continue;
339 }
340
341 if (conn == null) {
342 conn = datasource.getConnection();
343 }
344
345 if (selectStmt == null) {
346 selectStmt = conn.prepareStatement(selectByPK);
347 }
348 selectStmt.setString(1, senderUser);
349 selectStmt.setString(2, senderHost);
350 selectStmt.setString(3, recipientUser);
351 selectStmt.setString(4, recipientHost);
352 selectRS = selectStmt.executeQuery();
353 if (selectRS.next()) {
354
355 continue;
356 }
357
358 if (insertStmt == null) {
359 insertStmt = conn.prepareStatement(insert);
360 }
361 insertStmt.setString(1, senderUser);
362 insertStmt.setString(2, senderHost);
363 insertStmt.setString(3, recipientUser);
364 insertStmt.setString(4, recipientHost);
365 insertStmt.executeUpdate();
366 dbUpdated = true;
367
368 } finally {
369 theJDBCUtil.closeJDBCResultSet(selectRS);
370 }
371
372
373 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
374 conn.commit();
375 dbUpdated = false;
376 }
377 }
378 } catch (SQLException sqle) {
379 log("Error accessing database", sqle);
380 throw new MessagingException("Exception thrown", sqle);
381 } finally {
382 theJDBCUtil.closeJDBCStatement(selectStmt);
383 theJDBCUtil.closeJDBCStatement(insertStmt);
384
385 try {
386 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
387 conn.rollback();
388 dbUpdated = false;
389 }
390 }
391 catch (Exception e) {}
392 theJDBCUtil.closeJDBCConnection(conn);
393 }
394 }
395
396
397
398 private void manageDisplayRequest(Mail mail)
399 throws MessagingException {
400 MailAddress senderMailAddress = mail.getSender();
401 String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
402 String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
403
404 senderUser = getPrimaryName(senderUser);
405
406 Connection conn = null;
407 PreparedStatement selectStmt = null;
408 ResultSet selectRS = null;
409
410 StringWriter sout = new StringWriter();
411 PrintWriter out = new PrintWriter(sout, true);
412
413 try {
414 out.println("Answering on behalf of: " + whitelistManagerAddress);
415 out.println("Displaying white list of " + (new MailAddress(senderUser, senderHost)) + ":");
416 out.println();
417
418 conn = datasource.getConnection();
419 selectStmt = conn.prepareStatement(selectBySender);
420 selectStmt.setString(1, senderUser);
421 selectStmt.setString(2, senderHost);
422 selectRS = selectStmt.executeQuery();
423 while (selectRS.next()) {
424 MailAddress mailAddress =
425 new MailAddress(selectRS.getString(1), selectRS.getString(2));
426 out.println(mailAddress.toInternetAddress().toString());
427 }
428
429 out.println();
430 out.println("Finished");
431
432 sendReplyFromPostmaster(mail, sout.toString());
433
434 } catch (SQLException sqle) {
435 out.println("Error accessing the database");
436 sendReplyFromPostmaster(mail, sout.toString());
437 throw new MessagingException("Error accessing database", sqle);
438 } finally {
439 theJDBCUtil.closeJDBCResultSet(selectRS);
440 theJDBCUtil.closeJDBCStatement(selectStmt);
441 theJDBCUtil.closeJDBCConnection(conn);
442 }
443 }
444
445
446
447 private void manageInsertRequest(Mail mail)
448 throws MessagingException {
449 MailAddress senderMailAddress = mail.getSender();
450 String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
451 String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
452
453 senderUser = getPrimaryName(senderUser);
454
455 Connection conn = null;
456 PreparedStatement selectStmt = null;
457 PreparedStatement insertStmt = null;
458 boolean dbUpdated = false;
459
460 StringWriter sout = new StringWriter();
461 PrintWriter out = new PrintWriter(sout, true);
462
463 try {
464 out.println("Answering on behalf of: " + whitelistManagerAddress);
465 out.println("Inserting in the white list of " + (new MailAddress(senderUser, senderHost)) + " ...");
466 out.println();
467
468 MimeMessage message = mail.getMessage() ;
469
470 Object content= message.getContent();
471
472 if (message.getContentType().startsWith("text/plain")
473 && content instanceof String) {
474 StringTokenizer st = new StringTokenizer((String) content, " \t\n\r\f,;:<>");
475 while (st.hasMoreTokens()) {
476 ResultSet selectRS = null;
477 try {
478 MailAddress recipientMailAddress;
479 try {
480 recipientMailAddress = new MailAddress(st.nextToken());
481 }
482 catch (javax.mail.internet.ParseException pe) {
483 continue;
484 }
485 String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
486 String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
487
488 if (getMailetContext().isLocalServer(recipientHost)) {
489
490 continue;
491 }
492
493 if (conn == null) {
494 conn = datasource.getConnection();
495 }
496
497 if (selectStmt == null) {
498 selectStmt = conn.prepareStatement(selectByPK);
499 }
500 selectStmt.setString(1, senderUser);
501 selectStmt.setString(2, senderHost);
502 selectStmt.setString(3, recipientUser);
503 selectStmt.setString(4, recipientHost);
504 selectRS = selectStmt.executeQuery();
505 if (selectRS.next()) {
506
507 out.println("Skipped: " + recipientMailAddress);
508 continue;
509 }
510
511 if (insertStmt == null) {
512 insertStmt = conn.prepareStatement(insert);
513 }
514 insertStmt.setString(1, senderUser);
515 insertStmt.setString(2, senderHost);
516 insertStmt.setString(3, recipientUser);
517 insertStmt.setString(4, recipientHost);
518 insertStmt.executeUpdate();
519 dbUpdated = true;
520 out.println("Inserted: " + recipientMailAddress);
521
522 } finally {
523 theJDBCUtil.closeJDBCResultSet(selectRS);
524 }
525 }
526
527 if (dbUpdated) {
528 log("Insertion request issued by " + senderMailAddress);
529 }
530
531 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
532 conn.commit() ;
533 dbUpdated = false;
534 }
535 }
536 else {
537 out.println("The message must be plain - no action");
538 }
539
540 out.println();
541 out.println("Finished");
542
543 sendReplyFromPostmaster(mail, sout.toString());
544
545 } catch (SQLException sqle) {
546 out.println("Error accessing the database");
547 sendReplyFromPostmaster(mail, sout.toString());
548 throw new MessagingException("Error accessing the database", sqle);
549 } catch (IOException ioe) {
550 out.println("Error getting message content");
551 sendReplyFromPostmaster(mail, sout.toString());
552 throw new MessagingException("Error getting message content", ioe);
553 } finally {
554 theJDBCUtil.closeJDBCStatement(selectStmt);
555 theJDBCUtil.closeJDBCStatement(insertStmt);
556
557 try {
558 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
559 conn.rollback() ;
560 dbUpdated = false;
561 }
562 }
563 catch (Exception e) {}
564 theJDBCUtil.closeJDBCConnection(conn);
565 }
566 }
567
568
569
570 private void manageRemoveRequest(Mail mail)
571 throws MessagingException {
572 MailAddress senderMailAddress = mail.getSender();
573 String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
574 String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
575
576 senderUser = getPrimaryName(senderUser);
577
578 Connection conn = null;
579 PreparedStatement selectStmt = null;
580 PreparedStatement deleteStmt = null;
581 boolean dbUpdated = false;
582
583 StringWriter sout = new StringWriter();
584 PrintWriter out = new PrintWriter(sout, true);
585
586 try {
587 out.println("Answering on behalf of: " + whitelistManagerAddress);
588 out.println("Removing from the white list of " + (new MailAddress(senderUser, senderHost)) + " ...");
589 out.println();
590
591 MimeMessage message = mail.getMessage() ;
592
593 Object content= message.getContent();
594
595 if (message.getContentType().startsWith("text/plain")
596 && content instanceof String) {
597 StringTokenizer st = new StringTokenizer((String) content, " \t\n\r\f,;:<>");
598 while (st.hasMoreTokens()) {
599 ResultSet selectRS = null;
600 try {
601 MailAddress recipientMailAddress;
602 try {
603 recipientMailAddress = new MailAddress(st.nextToken());
604 }
605 catch (javax.mail.internet.ParseException pe) {
606 continue;
607 }
608 String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
609 String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
610
611 if (getMailetContext().isLocalServer(recipientHost)) {
612
613 continue;
614 }
615
616 if (conn == null) {
617 conn = datasource.getConnection();
618 }
619
620 if (selectStmt == null) {
621 selectStmt = conn.prepareStatement(selectByPK);
622 }
623 selectStmt.setString(1, senderUser);
624 selectStmt.setString(2, senderHost);
625 selectStmt.setString(3, recipientUser);
626 selectStmt.setString(4, recipientHost);
627 selectRS = selectStmt.executeQuery();
628 if (!selectRS.next()) {
629
630 out.println("Skipped: " + recipientMailAddress);
631 continue;
632 }
633
634 if (deleteStmt == null) {
635 deleteStmt = conn.prepareStatement(deleteByPK);
636 }
637 deleteStmt.setString(1, senderUser);
638 deleteStmt.setString(2, senderHost);
639 deleteStmt.setString(3, recipientUser);
640 deleteStmt.setString(4, recipientHost);
641 deleteStmt.executeUpdate();
642 dbUpdated = true;
643 out.println("Removed: " + recipientMailAddress);
644
645 } finally {
646 theJDBCUtil.closeJDBCResultSet(selectRS);
647 }
648 }
649
650 if (dbUpdated) {
651 log("Removal request issued by " + senderMailAddress);
652 }
653
654 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
655 conn.commit() ;
656 dbUpdated = false;
657 }
658 }
659 else {
660 out.println("The message must be plain - no action");
661 }
662
663 out.println();
664 out.println("Finished");
665
666 sendReplyFromPostmaster(mail, sout.toString());
667
668 } catch (SQLException sqle) {
669 out.println("Error accessing the database");
670 sendReplyFromPostmaster(mail, sout.toString());
671 throw new MessagingException("Error accessing the database", sqle);
672 } catch (IOException ioe) {
673 out.println("Error getting message content");
674 sendReplyFromPostmaster(mail, sout.toString());
675 throw new MessagingException("Error getting message content", ioe);
676 } finally {
677 theJDBCUtil.closeJDBCStatement(selectStmt);
678 theJDBCUtil.closeJDBCStatement(deleteStmt);
679
680 try {
681 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
682 conn.rollback() ;
683 dbUpdated = false;
684 }
685 }
686 catch (Exception e) {}
687 theJDBCUtil.closeJDBCConnection(conn);
688 }
689 }
690
691 private void sendReplyFromPostmaster(Mail mail, String stringContent) throws MessagingException {
692 try {
693 MailAddress notifier = getMailetContext().getPostmaster();
694
695 MailAddress senderMailAddress = mail.getSender();
696
697 MimeMessage message = mail.getMessage();
698
699 MimeMessage reply = new MimeMessage(Session.getDefaultInstance(System.getProperties(), null));
700
701
702 InternetAddress[] rcptAddr = new InternetAddress[1];
703 rcptAddr[0] = senderMailAddress.toInternetAddress();
704 reply.setRecipients(Message.RecipientType.TO, rcptAddr);
705
706
707 reply.setFrom(notifier.toInternetAddress());
708
709
710 MimeMultipart multipart = new MimeMultipart();
711
712 MimeBodyPart part = new MimeBodyPart();
713 part.setContent(stringContent, "text/plain");
714 part.setHeader(RFC2822Headers.CONTENT_TYPE, "text/plain");
715 multipart.addBodyPart(part);
716
717 reply.setContent(multipart);
718 reply.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
719
720
721 Set recipients = new HashSet();
722 recipients.add(senderMailAddress);
723
724
725 if (reply.getHeader(RFC2822Headers.DATE)==null){
726 reply.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new java.util.Date()));
727 }
728 String subject = message.getSubject();
729 if (subject == null) {
730 subject = "";
731 }
732 if (subject.indexOf("Re:") == 0){
733 reply.setSubject(subject);
734 } else {
735 reply.setSubject("Re:" + subject);
736 }
737 reply.setHeader(RFC2822Headers.IN_REPLY_TO, message.getMessageID());
738
739
740 getMailetContext().sendMail(notifier, recipients, reply);
741 }
742 catch (Exception e) {
743 log("Exception found sending reply", e);
744 }
745 }
746
747
748 private String getPrimaryName(String originalUsername) {
749 String username;
750 try {
751 username = localusers.getRealName(originalUsername);
752 JamesUser user = (JamesUser) localusers.getUserByName(username);
753 if (user.getAliasing()) {
754 username = user.getAlias();
755 }
756 }
757 catch (Exception e) {
758 username = originalUsername;
759 }
760 return username;
761 }
762
763
764
765
766
767
768
769
770
771 private void initSqlQueries(Connection conn, org.apache.mailet.MailetContext mailetContext) throws Exception {
772 try {
773 if (conn.getAutoCommit()) {
774 conn.setAutoCommit(false);
775 }
776
777 this.sqlFile = new File((String) mailetContext.getAttribute("confDir"), "sqlResources.xml").getCanonicalFile();
778 sqlQueries.init(this.sqlFile, "WhiteList" , conn, getSqlParameters());
779
780 checkTables(conn);
781 } finally {
782 theJDBCUtil.closeJDBCConnection(conn);
783 }
784 }
785
786 private void checkTables(Connection conn) throws SQLException {
787
788
789
790
791 boolean dbUpdated = false;
792
793 dbUpdated = createTable(conn, "whiteListTableName", "createWhiteListTable");
794
795
796 if (conn != null && dbUpdated && !conn.getAutoCommit()) {
797 conn.commit();
798 dbUpdated = false;
799 }
800
801 }
802
803 private boolean createTable(Connection conn, String tableNameSqlStringName, String createSqlStringName) throws SQLException {
804 String tableName = sqlQueries.getSqlString(tableNameSqlStringName, true);
805
806 DatabaseMetaData dbMetaData = conn.getMetaData();
807
808
809 if (theJDBCUtil.tableExists(dbMetaData, tableName)) {
810 return false;
811 }
812
813 PreparedStatement createStatement = null;
814
815 try {
816 createStatement =
817 conn.prepareStatement(sqlQueries.getSqlString(createSqlStringName, true));
818 createStatement.execute();
819
820 StringBuffer logBuffer = null;
821 logBuffer =
822 new StringBuffer(64)
823 .append("Created table '")
824 .append(tableName)
825 .append("' using sqlResources string '")
826 .append(createSqlStringName)
827 .append("'.");
828 log(logBuffer.toString());
829
830 } finally {
831 theJDBCUtil.closeJDBCStatement(createStatement);
832 }
833
834 return true;
835 }
836
837 }
838