diff -r 9f0b95aafaa3 -r ed84c8bdd87b org/sonews/daemon/command/PostCommand.java
--- a/org/sonews/daemon/command/PostCommand.java Sun Aug 29 17:04:25 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,332 +0,0 @@
-/*
- * SONEWS News Server
- * see AUTHORS for the list of contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sonews.daemon.command;
-
-import java.io.IOException;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.sql.SQLException;
-import java.util.Arrays;
-import javax.mail.MessagingException;
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetHeaders;
-import org.sonews.config.Config;
-import org.sonews.util.Log;
-import org.sonews.mlgw.Dispatcher;
-import org.sonews.storage.Article;
-import org.sonews.storage.Group;
-import org.sonews.daemon.NNTPConnection;
-import org.sonews.storage.Headers;
-import org.sonews.storage.StorageBackendException;
-import org.sonews.storage.StorageManager;
-import org.sonews.feed.FeedManager;
-import org.sonews.util.Stats;
-
-/**
- * Implementation of the POST command. This command requires multiple lines
- * from the client, so the handling of asynchronous reading is a little tricky
- * to handle.
- * @author Christian Lins
- * @since sonews/0.5.0
- */
-public class PostCommand implements Command
-{
-
- private final Article article = new Article();
- private int lineCount = 0;
- private long bodySize = 0;
- private InternetHeaders headers = null;
- private long maxBodySize =
- Config.inst().get(Config.ARTICLE_MAXSIZE, 128) * 1024L; // Size in bytes
- private PostState state = PostState.WaitForLineOne;
- private final ByteArrayOutputStream bufBody = new ByteArrayOutputStream();
- private final StringBuilder strHead = new StringBuilder();
-
- @Override
- public String[] getSupportedCommandStrings()
- {
- return new String[]{"POST"};
- }
-
- @Override
- public boolean hasFinished()
- {
- return this.state == PostState.Finished;
- }
-
- @Override
- public String impliedCapability()
- {
- return null;
- }
-
- @Override
- public boolean isStateful()
- {
- return true;
- }
-
- /**
- * Process the given line String. line.trim() was called by NNTPConnection.
- * @param line
- * @throws java.io.IOException
- * @throws java.sql.SQLException
- */
- @Override // TODO: Refactor this method to reduce complexity!
- public void processLine(NNTPConnection conn, String line, byte[] raw)
- throws IOException, StorageBackendException
- {
- switch(state)
- {
- case WaitForLineOne:
- {
- if(line.equalsIgnoreCase("POST"))
- {
- conn.println("340 send article to be posted. End with .");
- state = PostState.ReadingHeaders;
- }
- else
- {
- conn.println("500 invalid command usage");
- }
- break;
- }
- case ReadingHeaders:
- {
- strHead.append(line);
- strHead.append(NNTPConnection.NEWLINE);
-
- if("".equals(line) || ".".equals(line))
- {
- // we finally met the blank line
- // separating headers from body
-
- try
- {
- // Parse the header using the InternetHeader class from JavaMail API
- headers = new InternetHeaders(
- new ByteArrayInputStream(strHead.toString().trim()
- .getBytes(conn.getCurrentCharset())));
-
- // add the header entries for the article
- article.setHeaders(headers);
- }
- catch (MessagingException e)
- {
- e.printStackTrace();
- conn.println("500 posting failed - invalid header");
- state = PostState.Finished;
- break;
- }
-
- // Change charset for reading body;
- // for multipart messages UTF-8 is returned
- //conn.setCurrentCharset(article.getBodyCharset());
-
- state = PostState.ReadingBody;
-
- if(".".equals(line))
- {
- // Post an article without body
- postArticle(conn, article);
- state = PostState.Finished;
- }
- }
- break;
- }
- case ReadingBody:
- {
- if(".".equals(line))
- {
- // Set some headers needed for Over command
- headers.setHeader(Headers.LINES, Integer.toString(lineCount));
- headers.setHeader(Headers.BYTES, Long.toString(bodySize));
-
- byte[] body = bufBody.toByteArray();
- if(body.length >= 2)
- {
- // Remove trailing CRLF
- body = Arrays.copyOf(body, body.length - 2);
- }
- article.setBody(body); // set the article body
-
- postArticle(conn, article);
- state = PostState.Finished;
- }
- else
- {
- bodySize += line.length() + 1;
- lineCount++;
-
- // Add line to body buffer
- bufBody.write(raw, 0, raw.length);
- bufBody.write(NNTPConnection.NEWLINE.getBytes());
-
- if(bodySize > maxBodySize)
- {
- conn.println("500 article is too long");
- state = PostState.Finished;
- break;
- }
- }
- break;
- }
- default:
- {
- // Should never happen
- Log.get().severe("PostCommand::processLine(): already finished...");
- }
- }
- }
-
- /**
- * Article is a control message and needs special handling.
- * @param article
- */
- private void controlMessage(NNTPConnection conn, Article article)
- throws IOException
- {
- String[] ctrl = article.getHeader(Headers.CONTROL)[0].split(" ");
- if(ctrl.length == 2) // "cancel "
- {
- try
- {
- StorageManager.current().delete(ctrl[1]);
-
- // Move cancel message to "control" group
- article.setHeader(Headers.NEWSGROUPS, "control");
- StorageManager.current().addArticle(article);
- conn.println("240 article cancelled");
- }
- catch(StorageBackendException ex)
- {
- Log.get().severe(ex.toString());
- conn.println("500 internal server error");
- }
- }
- else
- {
- conn.println("441 unknown control header");
- }
- }
-
- private void supersedeMessage(NNTPConnection conn, Article article)
- throws IOException
- {
- try
- {
- String oldMsg = article.getHeader(Headers.SUPERSEDES)[0];
- StorageManager.current().delete(oldMsg);
- StorageManager.current().addArticle(article);
- conn.println("240 article replaced");
- }
- catch(StorageBackendException ex)
- {
- Log.get().severe(ex.toString());
- conn.println("500 internal server error");
- }
- }
-
- private void postArticle(NNTPConnection conn, Article article)
- throws IOException
- {
- if(article.getHeader(Headers.CONTROL)[0].length() > 0)
- {
- controlMessage(conn, article);
- }
- else if(article.getHeader(Headers.SUPERSEDES)[0].length() > 0)
- {
- supersedeMessage(conn, article);
- }
- else // Post the article regularily
- {
- // Circle check; note that Path can already contain the hostname here
- String host = Config.inst().get(Config.HOSTNAME, "localhost");
- if(article.getHeader(Headers.PATH)[0].indexOf(host + "!", 1) > 0)
- {
- Log.get().info(article.getMessageID() + " skipped for host " + host);
- conn.println("441 I know this article already");
- return;
- }
-
- // Try to create the article in the database or post it to
- // appropriate mailing list
- try
- {
- boolean success = false;
- String[] groupnames = article.getHeader(Headers.NEWSGROUPS)[0].split(",");
- for(String groupname : groupnames)
- {
- Group group = StorageManager.current().getGroup(groupname);
- if(group != null && !group.isDeleted())
- {
- if(group.isMailingList() && !conn.isLocalConnection())
- {
- // Send to mailing list; the Dispatcher writes
- // statistics to database
- Dispatcher.toList(article, group.getName());
- success = true;
- }
- else
- {
- // Store in database
- if(!StorageManager.current().isArticleExisting(article.getMessageID()))
- {
- StorageManager.current().addArticle(article);
-
- // Log this posting to statistics
- Stats.getInstance().mailPosted(
- article.getHeader(Headers.NEWSGROUPS)[0]);
- }
- success = true;
- }
- }
- } // end for
-
- if(success)
- {
- conn.println("240 article posted ok");
- FeedManager.queueForPush(article);
- }
- else
- {
- conn.println("441 newsgroup not found");
- }
- }
- catch(AddressException ex)
- {
- Log.get().warning(ex.getMessage());
- conn.println("441 invalid sender address");
- }
- catch(MessagingException ex)
- {
- // A MessageException is thrown when the sender email address is
- // invalid or something is wrong with the SMTP server.
- System.err.println(ex.getLocalizedMessage());
- conn.println("441 " + ex.getClass().getCanonicalName() + ": " + ex.getLocalizedMessage());
- }
- catch(StorageBackendException ex)
- {
- ex.printStackTrace();
- conn.println("500 internal server error");
- }
- }
- }
-
-}