diff -r 2fdc9cc89502 -r bb6990c0dd1a org/sonews/mlgw/Dispatcher.java --- a/org/sonews/mlgw/Dispatcher.java Wed Jul 22 14:04:05 2009 +0200 +++ b/org/sonews/mlgw/Dispatcher.java Thu Aug 20 14:31:19 2009 +0200 @@ -21,6 +21,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.mail.Address; import javax.mail.Authenticator; import javax.mail.Message; @@ -29,6 +31,7 @@ import javax.mail.internet.InternetAddress; import org.sonews.config.Config; import org.sonews.storage.Article; +import org.sonews.storage.Group; import org.sonews.storage.Headers; import org.sonews.storage.StorageBackendException; import org.sonews.storage.StorageManager; @@ -36,7 +39,7 @@ import org.sonews.util.Stats; /** - * Dispatches messages from mailing list or newsserver or vice versa. + * Dispatches messages from mailing list to newsserver or vice versa. * @author Christian Lins * @since sonews/0.5.0 */ @@ -58,86 +61,160 @@ } } + + /** + * Chunks out the email address of the full List-Post header field. + * @param listPostValue + * @return The matching email address or null + */ + private static String chunkListPost(String listPostValue) + { + // listPostValue is of form "" + Pattern mailPattern = Pattern.compile("(\\w+[-|.])*\\w+@(\\w+.)+\\w+"); + Matcher mailMatcher = mailPattern.matcher(listPostValue); + if(mailMatcher.find()) + { + return listPostValue.substring(mailMatcher.start(), mailMatcher.end()); + } + else + { + return null; + } + } + + /** + * This method inspects the header of the given message, trying + * to find the most appropriate recipient. + * @param msg + * @param fallback If this is false only List-Post and X-List-Post headers + * are examined. + * @return null or fitting group name for the given message. + */ + private static List getGroupFor(final Message msg, final boolean fallback) + throws MessagingException, StorageBackendException + { + List groups = null; + + // Is there a List-Post header? + String[] listPost = msg.getHeader(Headers.LIST_POST); + InternetAddress listPostAddr; + + if(listPost == null || listPost.length == 0 || "".equals(listPost[0])) + { + // Is there a X-List-Post header? + listPost = msg.getHeader(Headers.X_LIST_POST); + } + + if(listPost != null && listPost.length > 0 + && !"".equals(listPost[0]) && chunkListPost(listPost[0]) != null) + { + // listPost[0] is of form "" + listPost[0] = chunkListPost(listPost[0]); + listPostAddr = new InternetAddress(listPost[0], false); + groups = StorageManager.current().getGroupsForList(listPostAddr); + } + else if(fallback) + { + Log.msg("Using fallback recipient discovery for: " + msg.getSubject(), true); + groups = new ArrayList(); + // Fallback to TO/CC/BCC addresses + Address[] to = msg.getAllRecipients(); + for(Address toa : to) // Address can have '<' '>' around + { + if(toa instanceof InternetAddress) + { + List g = StorageManager.current().getGroupsForList((InternetAddress) toa); + groups.addAll(g); + } + } + } + + return groups; + } /** * Posts a message that was received from a mailing list to the * appropriate newsgroup. + * If the message already exists in the storage, this message checks + * if it must be posted in an additional group. This can happen for + * crosspostings in different mailing lists. * @param msg */ public static boolean toGroup(final Message msg) { try { - Address[] to = msg.getAllRecipients(); // includes TO/CC/BCC - if(to == null || to.length <= 0) + // Create new Article object + Article article = new Article(msg); + boolean posted = false; + + // Check if this mail is already existing the storage + boolean updateReq = + StorageManager.current().isArticleExisting(article.getMessageID()); + + List newsgroups = getGroupFor(msg, !updateReq); + List oldgroups = new ArrayList(); + if(updateReq) { - to = msg.getReplyTo(); + // Check for duplicate entries of the same group + Article oldArticle = StorageManager.current().getArticle(article.getMessageID()); + List oldGroups = oldArticle.getGroups(); + for(Group oldGroup : oldGroups) + { + if(!newsgroups.contains(oldGroup.getName())) + { + oldgroups.add(oldGroup.getName()); + } + } } - if(to == null || to.length <= 0) + if(newsgroups.size() > 0) { - Log.msg("Skipping message because no recipient!", false); - return false; + newsgroups.addAll(oldgroups); + StringBuilder groups = new StringBuilder(); + for(int n = 0; n < newsgroups.size(); n++) + { + groups.append(newsgroups.get(n)); + if (n + 1 != newsgroups.size()) + { + groups.append(','); + } + } + Log.msg("Posting to group " + groups.toString(), true); + + article.setGroup(groups.toString()); + //article.removeHeader(Headers.REPLY_TO); + //article.removeHeader(Headers.TO); + + // Write article to database + if(updateReq) + { + Log.msg("Updating " + article.getMessageID() + " with additional groups", true); + StorageManager.current().delete(article.getMessageID()); + StorageManager.current().addArticle(article); + } + else + { + Log.msg("Gatewaying " + article.getMessageID() + " to " + + article.getHeader(Headers.NEWSGROUPS)[0], true); + StorageManager.current().addArticle(article); + Stats.getInstance().mailGatewayed( + article.getHeader(Headers.NEWSGROUPS)[0]); + } + posted = true; } else { - boolean posted = false; - List newsgroups = new ArrayList(); - - for (Address toa : to) // Address can have '<' '>' around + StringBuilder buf = new StringBuilder(); + for (Address toa : msg.getAllRecipients()) { - if (toa instanceof InternetAddress) - { - List groups = StorageManager.current() - .getGroupsForList((InternetAddress)toa); - newsgroups.addAll(groups); - } + buf.append(' '); + buf.append(toa.toString()); } - - if (newsgroups.size() > 0) - { - StringBuilder groups = new StringBuilder(); - for(int n = 0; n < newsgroups.size(); n++) - { - groups.append(newsgroups.get(n)); - if(n + 1 != newsgroups.size()) - { - groups.append(','); - } - } - Log.msg("Posting to group " + groups.toString(), true); - - // Create new Article object - Article article = new Article(msg); - article.setGroup(groups.toString()); - article.removeHeader(Headers.REPLY_TO); - article.removeHeader(Headers.TO); - - // Write article to database - if(!StorageManager.current().isArticleExisting(article.getMessageID())) - { - StorageManager.current().addArticle(article); - Stats.getInstance().mailGatewayed( - article.getHeader(Headers.NEWSGROUPS)[0]); - } - else - { - Log.msg("Article " + article.getMessageID() + " already existing.", true); - } - posted = true; - } - else - { - StringBuilder buf = new StringBuilder(); - for(Address toa : to) - { - buf.append(' '); - buf.append(toa.toString()); - } - Log.msg("No group for" + buf.toString(), false); - } - return posted; + buf.append(" " + article.getHeader(Headers.LIST_POST)[0]); + Log.msg("No group for" + buf.toString(), false); } + return posted; } catch(Exception ex) { @@ -148,56 +225,53 @@ /** * Mails a message received through NNTP to the appropriate mailing list. + * This method MAY be called several times by PostCommand for the same + * article. */ - public static void toList(Article article) + public static void toList(Article article, String group) throws IOException, MessagingException, StorageBackendException { // Get mailing lists for the group of this article - List listAddresses = new ArrayList(); - String[] groupnames = article.getHeader(Headers.NEWSGROUPS)[0].split(","); - - for(String groupname : groupnames) + List rcptAddresses = StorageManager.current().getListsForGroup(group); + + if(rcptAddresses == null || rcptAddresses.size() == 0) { - String listAddress = StorageManager.current().getListForGroup(groupname); - if(listAddress != null) - { - listAddresses.add(listAddress); - } + Log.msg("No ML-address for " + group + " found.", false); + return; } - for(String listAddress : listAddresses) + for(String rcptAddress : rcptAddresses) { // Compose message and send it via given SMTP-Host String smtpHost = Config.inst().get(Config.MLSEND_HOST, "localhost"); - int smtpPort = Config.inst().get(Config.MLSEND_PORT, 25); + int smtpPort = Config.inst().get(Config.MLSEND_PORT, 25); String smtpUser = Config.inst().get(Config.MLSEND_USER, "user"); - String smtpPw = Config.inst().get(Config.MLSEND_PASSWORD, "mysecret"); + String smtpPw = Config.inst().get(Config.MLSEND_PASSWORD, "mysecret"); String smtpFrom = Config.inst().get( - Config.MLSEND_ADDRESS, article.getHeader(Headers.FROM)[0]); + Config.MLSEND_ADDRESS, article.getHeader(Headers.FROM)[0]); // TODO: Make Article cloneable() - String group = article.getHeader(Headers.NEWSGROUPS)[0]; article.getMessageID(); // Make sure an ID is existing article.removeHeader(Headers.NEWSGROUPS); article.removeHeader(Headers.PATH); article.removeHeader(Headers.LINES); article.removeHeader(Headers.BYTES); - article.setHeader("To", listAddress); - article.setHeader("Reply-To", listAddress); + article.setHeader("To", rcptAddress); + //article.setHeader("Reply-To", listAddress); - if(Config.inst().get(Config.MLSEND_RW_SENDER, false)) + if (Config.inst().get(Config.MLSEND_RW_SENDER, false)) { rewriteSenderAddress(article); // Set the SENDER address } SMTPTransport smtpTransport = new SMTPTransport(smtpHost, smtpPort); - smtpTransport.send(article, smtpFrom, listAddress); + smtpTransport.send(article, smtpFrom, rcptAddress); smtpTransport.close(); Stats.getInstance().mailGatewayed(group); - Log.msg("MLGateway: Mail " + article.getHeader("Subject")[0] - + " was delivered to " + listAddress + ".", true); + Log.msg("MLGateway: Mail " + article.getHeader("Subject")[0] + + " was delivered to " + rcptAddress + ".", true); } }