org/sonews/daemon/command/PostCommand.java
changeset 7 0b76e099eb96
parent 1 6fceb66e1ad7
child 12 bb6990c0dd1a
     1.1 --- a/org/sonews/daemon/command/PostCommand.java	Fri Jun 26 16:48:50 2009 +0200
     1.2 +++ b/org/sonews/daemon/command/PostCommand.java	Wed Aug 12 13:03:23 2009 +0200
     1.3 @@ -19,24 +19,26 @@
     1.4  package org.sonews.daemon.command;
     1.5  
     1.6  import java.io.IOException;
     1.7 -
     1.8  import java.io.ByteArrayInputStream;
     1.9 +import java.io.ByteArrayOutputStream;
    1.10  import java.nio.charset.Charset;
    1.11  import java.nio.charset.IllegalCharsetNameException;
    1.12  import java.nio.charset.UnsupportedCharsetException;
    1.13  import java.sql.SQLException;
    1.14 +import java.util.Arrays;
    1.15  import java.util.Locale;
    1.16  import javax.mail.MessagingException;
    1.17  import javax.mail.internet.AddressException;
    1.18  import javax.mail.internet.InternetHeaders;
    1.19 -import org.sonews.daemon.Config;
    1.20 +import org.sonews.config.Config;
    1.21  import org.sonews.util.Log;
    1.22  import org.sonews.mlgw.Dispatcher;
    1.23 -import org.sonews.daemon.storage.Article;
    1.24 -import org.sonews.daemon.storage.Database;
    1.25 -import org.sonews.daemon.storage.Group;
    1.26 +import org.sonews.storage.Article;
    1.27 +import org.sonews.storage.Group;
    1.28  import org.sonews.daemon.NNTPConnection;
    1.29 -import org.sonews.daemon.storage.Headers;
    1.30 +import org.sonews.storage.Headers;
    1.31 +import org.sonews.storage.StorageBackendException;
    1.32 +import org.sonews.storage.StorageManager;
    1.33  import org.sonews.feed.FeedManager;
    1.34  import org.sonews.util.Stats;
    1.35  
    1.36 @@ -47,7 +49,7 @@
    1.37   * @author Christian Lins
    1.38   * @since sonews/0.5.0
    1.39   */
    1.40 -public class PostCommand extends AbstractCommand
    1.41 +public class PostCommand implements Command
    1.42  {
    1.43    
    1.44    private final Article article   = new Article();
    1.45 @@ -55,14 +57,15 @@
    1.46    private long          bodySize  = 0;
    1.47    private InternetHeaders headers = null;
    1.48    private long          maxBodySize  = 
    1.49 -    Config.getInstance().get(Config.ARTICLE_MAXSIZE, 128) * 1024L; // Size in bytes
    1.50 +    Config.inst().get(Config.ARTICLE_MAXSIZE, 128) * 1024L; // Size in bytes
    1.51    private PostState     state     = PostState.WaitForLineOne;
    1.52 -  private final StringBuilder strBody   = new StringBuilder();
    1.53 -  private final StringBuilder strHead   = new StringBuilder();
    1.54 -  
    1.55 -  public PostCommand(final NNTPConnection conn)
    1.56 +  private final ByteArrayOutputStream bufBody   = new ByteArrayOutputStream();
    1.57 +  private final StringBuilder         strHead   = new StringBuilder();
    1.58 +
    1.59 +  @Override
    1.60 +  public String[] getSupportedCommandStrings()
    1.61    {
    1.62 -    super(conn);
    1.63 +    return new String[]{"POST"};
    1.64    }
    1.65  
    1.66    @Override
    1.67 @@ -71,6 +74,12 @@
    1.68      return this.state == PostState.Finished;
    1.69    }
    1.70  
    1.71 +  @Override
    1.72 +  public boolean isStateful()
    1.73 +  {
    1.74 +    return true;
    1.75 +  }
    1.76 +
    1.77    /**
    1.78     * Process the given line String. line.trim() was called by NNTPConnection.
    1.79     * @param line
    1.80 @@ -78,8 +87,8 @@
    1.81     * @throws java.sql.SQLException
    1.82     */
    1.83    @Override // TODO: Refactor this method to reduce complexity!
    1.84 -  public void processLine(String line)
    1.85 -    throws IOException, SQLException
    1.86 +  public void processLine(NNTPConnection conn, String line, byte[] raw)
    1.87 +    throws IOException, StorageBackendException
    1.88    {
    1.89      switch(state)
    1.90      {
    1.91 @@ -87,12 +96,12 @@
    1.92        {
    1.93          if(line.equalsIgnoreCase("POST"))
    1.94          {
    1.95 -          printStatus(340, "send article to be posted. End with <CR-LF>.<CR-LF>");
    1.96 +          conn.println("340 send article to be posted. End with <CR-LF>.<CR-LF>");
    1.97            state = PostState.ReadingHeaders;
    1.98          }
    1.99          else
   1.100          {
   1.101 -          printStatus(500, "invalid command usage");
   1.102 +          conn.println("500 invalid command usage");
   1.103          }
   1.104          break;
   1.105        }
   1.106 @@ -111,7 +120,7 @@
   1.107              // Parse the header using the InternetHeader class from JavaMail API
   1.108              headers = new InternetHeaders(
   1.109                new ByteArrayInputStream(strHead.toString().trim()
   1.110 -                .getBytes(connection.getCurrentCharset())));
   1.111 +                .getBytes(conn.getCurrentCharset())));
   1.112  
   1.113              // add the header entries for the article
   1.114              article.setHeaders(headers);
   1.115 @@ -119,21 +128,21 @@
   1.116            catch (MessagingException e)
   1.117            {
   1.118              e.printStackTrace();
   1.119 -            printStatus(500, "posting failed - invalid header");
   1.120 +            conn.println("500 posting failed - invalid header");
   1.121              state = PostState.Finished;
   1.122              break;
   1.123            }
   1.124  
   1.125            // Change charset for reading body; 
   1.126            // for multipart messages UTF-8 is returned
   1.127 -          connection.setCurrentCharset(article.getBodyCharset());
   1.128 +          //conn.setCurrentCharset(article.getBodyCharset());
   1.129            
   1.130            state = PostState.ReadingBody;
   1.131            
   1.132            if(".".equals(line))
   1.133            {
   1.134              // Post an article without body
   1.135 -            postArticle(article);
   1.136 +            postArticle(conn, article);
   1.137              state = PostState.Finished;
   1.138            }
   1.139          }
   1.140 @@ -146,15 +155,16 @@
   1.141            // Set some headers needed for Over command
   1.142            headers.setHeader(Headers.LINES, Integer.toString(lineCount));
   1.143            headers.setHeader(Headers.BYTES, Long.toString(bodySize));
   1.144 +
   1.145 +          byte[] body = bufBody.toByteArray();
   1.146 +          if(body.length >= 2)
   1.147 +          {
   1.148 +            // Remove trailing CRLF
   1.149 +            body = Arrays.copyOf(body, body.length - 2);
   1.150 +          }
   1.151 +          article.setBody(body); // set the article body
   1.152            
   1.153 -          if(strBody.length() >= 2)
   1.154 -          {
   1.155 -            strBody.deleteCharAt(strBody.length() - 1); // Remove last newline
   1.156 -            strBody.deleteCharAt(strBody.length() - 1); // Remove last CR
   1.157 -          }
   1.158 -          article.setBody(strBody.toString()); // set the article body
   1.159 -          
   1.160 -          postArticle(article);
   1.161 +          postArticle(conn, article);
   1.162            state = PostState.Finished;
   1.163          }
   1.164          else
   1.165 @@ -163,19 +173,19 @@
   1.166            lineCount++;
   1.167            
   1.168            // Add line to body buffer
   1.169 -          strBody.append(line);
   1.170 -          strBody.append(NNTPConnection.NEWLINE);
   1.171 +          bufBody.write(raw, 0, raw.length);
   1.172 +          bufBody.write(NNTPConnection.NEWLINE.getBytes());
   1.173            
   1.174            if(bodySize > maxBodySize)
   1.175            {
   1.176 -            printStatus(500, "article is too long");
   1.177 +            conn.println("500 article is too long");
   1.178              state = PostState.Finished;
   1.179              break;
   1.180            }
   1.181            
   1.182            // Check if this message is a MIME-multipart message and needs a
   1.183            // charset change
   1.184 -          try
   1.185 +          /*try
   1.186            {
   1.187              line = line.toLowerCase(Locale.ENGLISH);
   1.188              if(line.startsWith(Headers.CONTENT_TYPE))
   1.189 @@ -197,7 +207,7 @@
   1.190  
   1.191                  try
   1.192                  {
   1.193 -                  connection.setCurrentCharset(Charset.forName(charsetName));
   1.194 +                  conn.setCurrentCharset(Charset.forName(charsetName));
   1.195                  }
   1.196                  catch(IllegalCharsetNameException ex)
   1.197                  {
   1.198 @@ -213,12 +223,15 @@
   1.199            catch(Exception ex)
   1.200            {
   1.201              ex.printStackTrace();
   1.202 -          }
   1.203 +          }*/
   1.204          }
   1.205          break;
   1.206        }
   1.207        default:
   1.208 +      {
   1.209 +        // Should never happen
   1.210          Log.msg("PostCommand::processLine(): already finished...", false);
   1.211 +      }
   1.212      }
   1.213    }
   1.214    
   1.215 @@ -226,7 +239,7 @@
   1.216     * Article is a control message and needs special handling.
   1.217     * @param article
   1.218     */
   1.219 -  private void controlMessage(Article article)
   1.220 +  private void controlMessage(NNTPConnection conn, Article article)
   1.221      throws IOException
   1.222    {
   1.223      String[] ctrl = article.getHeader(Headers.CONTROL)[0].split(" ");
   1.224 @@ -234,52 +247,52 @@
   1.225      {
   1.226        try
   1.227        {
   1.228 -        Database.getInstance().delete(ctrl[1]);
   1.229 +        StorageManager.current().delete(ctrl[1]);
   1.230          
   1.231          // Move cancel message to "control" group
   1.232          article.setHeader(Headers.NEWSGROUPS, "control");
   1.233 -        Database.getInstance().addArticle(article);
   1.234 -        printStatus(240, "article cancelled");
   1.235 +        StorageManager.current().addArticle(article);
   1.236 +        conn.println("240 article cancelled");
   1.237        }
   1.238 -      catch(SQLException ex)
   1.239 +      catch(StorageBackendException ex)
   1.240        {
   1.241          Log.msg(ex, false);
   1.242 -        printStatus(500, "internal server error");
   1.243 +        conn.println("500 internal server error");
   1.244        }
   1.245      }
   1.246      else
   1.247      {
   1.248 -      printStatus(441, "unknown Control header");
   1.249 +      conn.println("441 unknown control header");
   1.250      }
   1.251    }
   1.252    
   1.253 -  private void supersedeMessage(Article article)
   1.254 +  private void supersedeMessage(NNTPConnection conn, Article article)
   1.255      throws IOException
   1.256    {
   1.257      try
   1.258      {
   1.259        String oldMsg = article.getHeader(Headers.SUPERSEDES)[0];
   1.260 -      Database.getInstance().delete(oldMsg);
   1.261 -      Database.getInstance().addArticle(article);
   1.262 -      printStatus(240, "article replaced");
   1.263 +      StorageManager.current().delete(oldMsg);
   1.264 +      StorageManager.current().addArticle(article);
   1.265 +      conn.println("240 article replaced");
   1.266      }
   1.267 -    catch(SQLException ex)
   1.268 +    catch(StorageBackendException ex)
   1.269      {
   1.270        Log.msg(ex, false);
   1.271 -      printStatus(500, "internal server error");
   1.272 +      conn.println("500 internal server error");
   1.273      }
   1.274    }
   1.275    
   1.276 -  private void postArticle(Article article) 
   1.277 +  private void postArticle(NNTPConnection conn, Article article)
   1.278      throws IOException
   1.279    {
   1.280      if(article.getHeader(Headers.CONTROL)[0].length() > 0)
   1.281      {
   1.282 -      controlMessage(article);
   1.283 +      controlMessage(conn, article);
   1.284      }
   1.285      else if(article.getHeader(Headers.SUPERSEDES)[0].length() > 0)
   1.286      {
   1.287 -      supersedeMessage(article);
   1.288 +      supersedeMessage(conn, article);
   1.289      }
   1.290      else // Post the article regularily
   1.291      {
   1.292 @@ -291,10 +304,10 @@
   1.293          String[] groupnames = article.getHeader(Headers.NEWSGROUPS)[0].split(",");
   1.294          for(String groupname : groupnames)
   1.295          {
   1.296 -          Group group = Database.getInstance().getGroup(groupname);
   1.297 -          if(group != null)
   1.298 +          Group group = StorageManager.current().getGroup(groupname);
   1.299 +          if(group != null && !group.isDeleted())
   1.300            {
   1.301 -            if(group.isMailingList() && !connection.isLocalConnection())
   1.302 +            if(group.isMailingList() && !conn.isLocalConnection())
   1.303              {
   1.304                // Send to mailing list; the Dispatcher writes 
   1.305                // statistics to database
   1.306 @@ -304,9 +317,9 @@
   1.307              else
   1.308              {
   1.309                // Store in database
   1.310 -              if(!Database.getInstance().isArticleExisting(article.getMessageID()))
   1.311 +              if(!StorageManager.current().isArticleExisting(article.getMessageID()))
   1.312                {
   1.313 -                Database.getInstance().addArticle(article);
   1.314 +                StorageManager.current().addArticle(article);
   1.315  
   1.316                  // Log this posting to statistics
   1.317                  Stats.getInstance().mailPosted(
   1.318 @@ -319,30 +332,30 @@
   1.319  
   1.320          if(success)
   1.321          {
   1.322 -          printStatus(240, "article posted ok");
   1.323 +          conn.println("240 article posted ok");
   1.324            FeedManager.queueForPush(article);
   1.325          }
   1.326          else
   1.327          {
   1.328 -          printStatus(441, "newsgroup not found");
   1.329 +          conn.println("441 newsgroup not found");
   1.330          }
   1.331        }
   1.332        catch(AddressException ex)
   1.333        {
   1.334          Log.msg(ex.getMessage(), true);
   1.335 -        printStatus(441, "invalid sender address");
   1.336 +        conn.println("441 invalid sender address");
   1.337        }
   1.338        catch(MessagingException ex)
   1.339        {
   1.340          // A MessageException is thrown when the sender email address is
   1.341          // invalid or something is wrong with the SMTP server.
   1.342          System.err.println(ex.getLocalizedMessage());
   1.343 -        printStatus(441, ex.getClass().getCanonicalName() + ": " + ex.getLocalizedMessage());
   1.344 +        conn.println("441 " + ex.getClass().getCanonicalName() + ": " + ex.getLocalizedMessage());
   1.345        }
   1.346 -      catch(SQLException ex)
   1.347 +      catch(StorageBackendException ex)
   1.348        {
   1.349          ex.printStackTrace();
   1.350 -        printStatus(500, "internal server error");
   1.351 +        conn.println("500 internal server error");
   1.352        }
   1.353      }
   1.354    }