org/sonews/mlgw/Dispatcher.java
changeset 12 bb6990c0dd1a
parent 3 2fdc9cc89502
child 14 efce4ec25564
     1.1 --- a/org/sonews/mlgw/Dispatcher.java	Wed Jul 22 14:04:05 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