diff -r 9f0b95aafaa3 -r ed84c8bdd87b src/org/sonews/storage/Article.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/sonews/storage/Article.java Sun Aug 29 17:28:58 2010 +0200
@@ -0,0 +1,253 @@
+/*
+ * 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.storage;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.UUID;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import javax.mail.Header;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetHeaders;
+import org.sonews.config.Config;
+
+/**
+ * Represents a newsgroup article.
+ * @author Christian Lins
+ * @author Denis Schwerdel
+ * @since n3tpd/0.1
+ */
+public class Article extends ArticleHead
+{
+
+ /**
+ * Loads the Article identified by the given ID from the JDBCDatabase.
+ * @param messageID
+ * @return null if Article is not found or if an error occurred.
+ */
+ public static Article getByMessageID(final String messageID)
+ {
+ try
+ {
+ return StorageManager.current().getArticle(messageID);
+ }
+ catch(StorageBackendException ex)
+ {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ private byte[] body = new byte[0];
+
+ /**
+ * Default constructor.
+ */
+ public Article()
+ {
+ }
+
+ /**
+ * Creates a new Article object using the date from the given
+ * raw data.
+ */
+ public Article(String headers, byte[] body)
+ {
+ try
+ {
+ this.body = body;
+
+ // Parse the header
+ this.headers = new InternetHeaders(
+ new ByteArrayInputStream(headers.getBytes()));
+
+ this.headerSrc = headers;
+ }
+ catch(MessagingException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Creates an Article instance using the data from the javax.mail.Message
+ * object. This constructor is called by the Mailinglist gateway.
+ * @see javax.mail.Message
+ * @param msg
+ * @throws IOException
+ * @throws MessagingException
+ */
+ public Article(final Message msg)
+ throws IOException, MessagingException
+ {
+ this.headers = new InternetHeaders();
+
+ for(Enumeration e = msg.getAllHeaders() ; e.hasMoreElements();)
+ {
+ final Header header = (Header)e.nextElement();
+ this.headers.addHeader(header.getName(), header.getValue());
+ }
+
+ // Reads the raw byte body using Message.writeTo(OutputStream out)
+ this.body = readContent(msg);
+
+ // Validate headers
+ validateHeaders();
+ }
+
+ /**
+ * Reads from the given Message into a byte array.
+ * @param in
+ * @return
+ * @throws IOException
+ */
+ private byte[] readContent(Message in)
+ throws IOException, MessagingException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ in.writeTo(out);
+ return out.toByteArray();
+ }
+
+ /**
+ * Removes the header identified by the given key.
+ * @param headerKey
+ */
+ public void removeHeader(final String headerKey)
+ {
+ this.headers.removeHeader(headerKey);
+ this.headerSrc = null;
+ }
+
+ /**
+ * Generates a message id for this article and sets it into
+ * the header object. You have to update the JDBCDatabase manually to make this
+ * change persistent.
+ * Note: a Message-ID should never be changed and only generated once.
+ */
+ private String generateMessageID()
+ {
+ String randomString;
+ MessageDigest md5;
+ try
+ {
+ md5 = MessageDigest.getInstance("MD5");
+ md5.reset();
+ md5.update(getBody());
+ md5.update(getHeader(Headers.SUBJECT)[0].getBytes());
+ md5.update(getHeader(Headers.FROM)[0].getBytes());
+ byte[] result = md5.digest();
+ StringBuffer hexString = new StringBuffer();
+ for (int i = 0; i < result.length; i++)
+ {
+ hexString.append(Integer.toHexString(0xFF & result[i]));
+ }
+ randomString = hexString.toString();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ randomString = UUID.randomUUID().toString();
+ }
+ String msgID = "<" + randomString + "@"
+ + Config.inst().get(Config.HOSTNAME, "localhost") + ">";
+
+ this.headers.setHeader(Headers.MESSAGE_ID, msgID);
+
+ return msgID;
+ }
+
+ /**
+ * Returns the body string.
+ */
+ public byte[] getBody()
+ {
+ return body;
+ }
+
+ /**
+ * @return Numerical IDs of the newsgroups this Article belongs to.
+ */
+ public List getGroups()
+ {
+ String[] groupnames = getHeader(Headers.NEWSGROUPS)[0].split(",");
+ ArrayList groups = new ArrayList();
+
+ try
+ {
+ for(String newsgroup : groupnames)
+ {
+ newsgroup = newsgroup.trim();
+ Group group = StorageManager.current().getGroup(newsgroup);
+ if(group != null && // If the server does not provide the group, ignore it
+ !groups.contains(group)) // Yes, there may be duplicates
+ {
+ groups.add(group);
+ }
+ }
+ }
+ catch(StorageBackendException ex)
+ {
+ ex.printStackTrace();
+ return null;
+ }
+ return groups;
+ }
+
+ public void setBody(byte[] body)
+ {
+ this.body = body;
+ }
+
+ /**
+ *
+ * @param groupname Name(s) of newsgroups
+ */
+ public void setGroup(String groupname)
+ {
+ this.headers.setHeader(Headers.NEWSGROUPS, groupname);
+ }
+
+ /**
+ * Returns the Message-ID of this Article. If the appropriate header
+ * is empty, a new Message-ID is created.
+ * @return Message-ID of this Article.
+ */
+ public String getMessageID()
+ {
+ String[] msgID = getHeader(Headers.MESSAGE_ID);
+ return msgID[0].equals("") ? generateMessageID() : msgID[0];
+ }
+
+ /**
+ * @return String containing the Message-ID.
+ */
+ @Override
+ public String toString()
+ {
+ return getMessageID();
+ }
+
+}