1.1 --- a/org/sonews/mlgw/Dispatcher.java Mon Aug 17 11:00:51 2009 +0200
1.2 +++ b/org/sonews/mlgw/Dispatcher.java Thu Aug 20 14:31:19 2009 +0200
1.3 @@ -21,6 +21,8 @@
1.4 import java.io.IOException;
1.5 import java.util.ArrayList;
1.6 import java.util.List;
1.7 +import java.util.regex.Matcher;
1.8 +import java.util.regex.Pattern;
1.9 import javax.mail.Address;
1.10 import javax.mail.Authenticator;
1.11 import javax.mail.Message;
1.12 @@ -29,6 +31,7 @@
1.13 import javax.mail.internet.InternetAddress;
1.14 import org.sonews.config.Config;
1.15 import org.sonews.storage.Article;
1.16 +import org.sonews.storage.Group;
1.17 import org.sonews.storage.Headers;
1.18 import org.sonews.storage.StorageBackendException;
1.19 import org.sonews.storage.StorageManager;
1.20 @@ -36,7 +39,7 @@
1.21 import org.sonews.util.Stats;
1.22
1.23 /**
1.24 - * Dispatches messages from mailing list or newsserver or vice versa.
1.25 + * Dispatches messages from mailing list to newsserver or vice versa.
1.26 * @author Christian Lins
1.27 * @since sonews/0.5.0
1.28 */
1.29 @@ -58,86 +61,160 @@
1.30 }
1.31
1.32 }
1.33 +
1.34 + /**
1.35 + * Chunks out the email address of the full List-Post header field.
1.36 + * @param listPostValue
1.37 + * @return The matching email address or null
1.38 + */
1.39 + private static String chunkListPost(String listPostValue)
1.40 + {
1.41 + // listPostValue is of form "<mailto:dev@openoffice.org>"
1.42 + Pattern mailPattern = Pattern.compile("(\\w+[-|.])*\\w+@(\\w+.)+\\w+");
1.43 + Matcher mailMatcher = mailPattern.matcher(listPostValue);
1.44 + if(mailMatcher.find())
1.45 + {
1.46 + return listPostValue.substring(mailMatcher.start(), mailMatcher.end());
1.47 + }
1.48 + else
1.49 + {
1.50 + return null;
1.51 + }
1.52 + }
1.53 +
1.54 + /**
1.55 + * This method inspects the header of the given message, trying
1.56 + * to find the most appropriate recipient.
1.57 + * @param msg
1.58 + * @param fallback If this is false only List-Post and X-List-Post headers
1.59 + * are examined.
1.60 + * @return null or fitting group name for the given message.
1.61 + */
1.62 + private static List<String> getGroupFor(final Message msg, final boolean fallback)
1.63 + throws MessagingException, StorageBackendException
1.64 + {
1.65 + List<String> groups = null;
1.66 +
1.67 + // Is there a List-Post header?
1.68 + String[] listPost = msg.getHeader(Headers.LIST_POST);
1.69 + InternetAddress listPostAddr;
1.70 +
1.71 + if(listPost == null || listPost.length == 0 || "".equals(listPost[0]))
1.72 + {
1.73 + // Is there a X-List-Post header?
1.74 + listPost = msg.getHeader(Headers.X_LIST_POST);
1.75 + }
1.76 +
1.77 + if(listPost != null && listPost.length > 0
1.78 + && !"".equals(listPost[0]) && chunkListPost(listPost[0]) != null)
1.79 + {
1.80 + // listPost[0] is of form "<mailto:dev@openoffice.org>"
1.81 + listPost[0] = chunkListPost(listPost[0]);
1.82 + listPostAddr = new InternetAddress(listPost[0], false);
1.83 + groups = StorageManager.current().getGroupsForList(listPostAddr);
1.84 + }
1.85 + else if(fallback)
1.86 + {
1.87 + Log.msg("Using fallback recipient discovery for: " + msg.getSubject(), true);
1.88 + groups = new ArrayList<String>();
1.89 + // Fallback to TO/CC/BCC addresses
1.90 + Address[] to = msg.getAllRecipients();
1.91 + for(Address toa : to) // Address can have '<' '>' around
1.92 + {
1.93 + if(toa instanceof InternetAddress)
1.94 + {
1.95 + List<String> g = StorageManager.current().getGroupsForList((InternetAddress) toa);
1.96 + groups.addAll(g);
1.97 + }
1.98 + }
1.99 + }
1.100 +
1.101 + return groups;
1.102 + }
1.103
1.104 /**
1.105 * Posts a message that was received from a mailing list to the
1.106 * appropriate newsgroup.
1.107 + * If the message already exists in the storage, this message checks
1.108 + * if it must be posted in an additional group. This can happen for
1.109 + * crosspostings in different mailing lists.
1.110 * @param msg
1.111 */
1.112 public static boolean toGroup(final Message msg)
1.113 {
1.114 try
1.115 {
1.116 - Address[] to = msg.getAllRecipients(); // includes TO/CC/BCC
1.117 - if(to == null || to.length <= 0)
1.118 + // Create new Article object
1.119 + Article article = new Article(msg);
1.120 + boolean posted = false;
1.121 +
1.122 + // Check if this mail is already existing the storage
1.123 + boolean updateReq =
1.124 + StorageManager.current().isArticleExisting(article.getMessageID());
1.125 +
1.126 + List<String> newsgroups = getGroupFor(msg, !updateReq);
1.127 + List<String> oldgroups = new ArrayList<String>();
1.128 + if(updateReq)
1.129 {
1.130 - to = msg.getReplyTo();
1.131 + // Check for duplicate entries of the same group
1.132 + Article oldArticle = StorageManager.current().getArticle(article.getMessageID());
1.133 + List<Group> oldGroups = oldArticle.getGroups();
1.134 + for(Group oldGroup : oldGroups)
1.135 + {
1.136 + if(!newsgroups.contains(oldGroup.getName()))
1.137 + {
1.138 + oldgroups.add(oldGroup.getName());
1.139 + }
1.140 + }
1.141 }
1.142
1.143 - if(to == null || to.length <= 0)
1.144 + if(newsgroups.size() > 0)
1.145 {
1.146 - Log.msg("Skipping message because no recipient!", false);
1.147 - return false;
1.148 + newsgroups.addAll(oldgroups);
1.149 + StringBuilder groups = new StringBuilder();
1.150 + for(int n = 0; n < newsgroups.size(); n++)
1.151 + {
1.152 + groups.append(newsgroups.get(n));
1.153 + if (n + 1 != newsgroups.size())
1.154 + {
1.155 + groups.append(',');
1.156 + }
1.157 + }
1.158 + Log.msg("Posting to group " + groups.toString(), true);
1.159 +
1.160 + article.setGroup(groups.toString());
1.161 + //article.removeHeader(Headers.REPLY_TO);
1.162 + //article.removeHeader(Headers.TO);
1.163 +
1.164 + // Write article to database
1.165 + if(updateReq)
1.166 + {
1.167 + Log.msg("Updating " + article.getMessageID() + " with additional groups", true);
1.168 + StorageManager.current().delete(article.getMessageID());
1.169 + StorageManager.current().addArticle(article);
1.170 + }
1.171 + else
1.172 + {
1.173 + Log.msg("Gatewaying " + article.getMessageID() + " to "
1.174 + + article.getHeader(Headers.NEWSGROUPS)[0], true);
1.175 + StorageManager.current().addArticle(article);
1.176 + Stats.getInstance().mailGatewayed(
1.177 + article.getHeader(Headers.NEWSGROUPS)[0]);
1.178 + }
1.179 + posted = true;
1.180 }
1.181 else
1.182 {
1.183 - boolean posted = false;
1.184 - List<String> newsgroups = new ArrayList<String>();
1.185 -
1.186 - for (Address toa : to) // Address can have '<' '>' around
1.187 + StringBuilder buf = new StringBuilder();
1.188 + for (Address toa : msg.getAllRecipients())
1.189 {
1.190 - if (toa instanceof InternetAddress)
1.191 - {
1.192 - List<String> groups = StorageManager.current()
1.193 - .getGroupsForList((InternetAddress)toa);
1.194 - newsgroups.addAll(groups);
1.195 - }
1.196 + buf.append(' ');
1.197 + buf.append(toa.toString());
1.198 }
1.199 -
1.200 - if (newsgroups.size() > 0)
1.201 - {
1.202 - StringBuilder groups = new StringBuilder();
1.203 - for(int n = 0; n < newsgroups.size(); n++)
1.204 - {
1.205 - groups.append(newsgroups.get(n));
1.206 - if(n + 1 != newsgroups.size())
1.207 - {
1.208 - groups.append(',');
1.209 - }
1.210 - }
1.211 - Log.msg("Posting to group " + groups.toString(), true);
1.212 -
1.213 - // Create new Article object
1.214 - Article article = new Article(msg);
1.215 - article.setGroup(groups.toString());
1.216 - article.removeHeader(Headers.REPLY_TO);
1.217 - article.removeHeader(Headers.TO);
1.218 -
1.219 - // Write article to database
1.220 - if(!StorageManager.current().isArticleExisting(article.getMessageID()))
1.221 - {
1.222 - StorageManager.current().addArticle(article);
1.223 - Stats.getInstance().mailGatewayed(
1.224 - article.getHeader(Headers.NEWSGROUPS)[0]);
1.225 - }
1.226 - else
1.227 - {
1.228 - Log.msg("Article " + article.getMessageID() + " already existing.", true);
1.229 - }
1.230 - posted = true;
1.231 - }
1.232 - else
1.233 - {
1.234 - StringBuilder buf = new StringBuilder();
1.235 - for(Address toa : to)
1.236 - {
1.237 - buf.append(' ');
1.238 - buf.append(toa.toString());
1.239 - }
1.240 - Log.msg("No group for" + buf.toString(), false);
1.241 - }
1.242 - return posted;
1.243 + buf.append(" " + article.getHeader(Headers.LIST_POST)[0]);
1.244 + Log.msg("No group for" + buf.toString(), false);
1.245 }
1.246 + return posted;
1.247 }
1.248 catch(Exception ex)
1.249 {
1.250 @@ -148,56 +225,53 @@
1.251
1.252 /**
1.253 * Mails a message received through NNTP to the appropriate mailing list.
1.254 + * This method MAY be called several times by PostCommand for the same
1.255 + * article.
1.256 */
1.257 - public static void toList(Article article)
1.258 + public static void toList(Article article, String group)
1.259 throws IOException, MessagingException, StorageBackendException
1.260 {
1.261 // Get mailing lists for the group of this article
1.262 - List<String> listAddresses = new ArrayList<String>();
1.263 - String[] groupnames = article.getHeader(Headers.NEWSGROUPS)[0].split(",");
1.264 -
1.265 - for(String groupname : groupnames)
1.266 + List<String> rcptAddresses = StorageManager.current().getListsForGroup(group);
1.267 +
1.268 + if(rcptAddresses == null || rcptAddresses.size() == 0)
1.269 {
1.270 - String listAddress = StorageManager.current().getListForGroup(groupname);
1.271 - if(listAddress != null)
1.272 - {
1.273 - listAddresses.add(listAddress);
1.274 - }
1.275 + Log.msg("No ML-address for " + group + " found.", false);
1.276 + return;
1.277 }
1.278
1.279 - for(String listAddress : listAddresses)
1.280 + for(String rcptAddress : rcptAddresses)
1.281 {
1.282 // Compose message and send it via given SMTP-Host
1.283 String smtpHost = Config.inst().get(Config.MLSEND_HOST, "localhost");
1.284 - int smtpPort = Config.inst().get(Config.MLSEND_PORT, 25);
1.285 + int smtpPort = Config.inst().get(Config.MLSEND_PORT, 25);
1.286 String smtpUser = Config.inst().get(Config.MLSEND_USER, "user");
1.287 - String smtpPw = Config.inst().get(Config.MLSEND_PASSWORD, "mysecret");
1.288 + String smtpPw = Config.inst().get(Config.MLSEND_PASSWORD, "mysecret");
1.289 String smtpFrom = Config.inst().get(
1.290 - Config.MLSEND_ADDRESS, article.getHeader(Headers.FROM)[0]);
1.291 + Config.MLSEND_ADDRESS, article.getHeader(Headers.FROM)[0]);
1.292
1.293 // TODO: Make Article cloneable()
1.294 - String group = article.getHeader(Headers.NEWSGROUPS)[0];
1.295 article.getMessageID(); // Make sure an ID is existing
1.296 article.removeHeader(Headers.NEWSGROUPS);
1.297 article.removeHeader(Headers.PATH);
1.298 article.removeHeader(Headers.LINES);
1.299 article.removeHeader(Headers.BYTES);
1.300
1.301 - article.setHeader("To", listAddress);
1.302 - article.setHeader("Reply-To", listAddress);
1.303 + article.setHeader("To", rcptAddress);
1.304 + //article.setHeader("Reply-To", listAddress);
1.305
1.306 - if(Config.inst().get(Config.MLSEND_RW_SENDER, false))
1.307 + if (Config.inst().get(Config.MLSEND_RW_SENDER, false))
1.308 {
1.309 rewriteSenderAddress(article); // Set the SENDER address
1.310 }
1.311
1.312 SMTPTransport smtpTransport = new SMTPTransport(smtpHost, smtpPort);
1.313 - smtpTransport.send(article, smtpFrom, listAddress);
1.314 + smtpTransport.send(article, smtpFrom, rcptAddress);
1.315 smtpTransport.close();
1.316
1.317 Stats.getInstance().mailGatewayed(group);
1.318 - Log.msg("MLGateway: Mail " + article.getHeader("Subject")[0]
1.319 - + " was delivered to " + listAddress + ".", true);
1.320 + Log.msg("MLGateway: Mail " + article.getHeader("Subject")[0]
1.321 + + " was delivered to " + rcptAddress + ".", true);
1.322 }
1.323 }
1.324