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 }