diff -r 1090e2141798 -r 2fdc9cc89502 org/sonews/daemon/storage/Article.java --- a/org/sonews/daemon/storage/Article.java Wed Jul 01 10:48:22 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,401 +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.storage; - -import org.sonews.daemon.Config; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.sql.SQLException; -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.Multipart; -import javax.mail.internet.InternetHeaders; -import javax.mail.internet.MimeUtility; -import org.sonews.util.Log; - -/** - * 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 Database. - * @param messageID - * @return null if Article is not found or if an error occurred. - */ - public static Article getByMessageID(final String messageID) - { - try - { - return Database.getInstance().getArticle(messageID); - } - catch(SQLException ex) - { - ex.printStackTrace(); - return null; - } - } - - public static Article getByArticleNumber(long articleIndex, Group group) - throws SQLException - { - return Database.getInstance().getArticle(articleIndex, group.getID()); - } - - private String body = ""; - private String headerSrc = null; - - /** - * Default constructor. - */ - public Article() - { - } - - /** - * Creates a new Article object using the date from the given - * raw data. - * This construction has only package visibility. - */ - Article(String headers, String 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. - * @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()); - } - - // The "content" of the message can be a String if it's a simple text/plain - // message, a Multipart object or an InputStream if the content is unknown. - final Object content = msg.getContent(); - if(content instanceof String) - { - this.body = (String)content; - } - else if(content instanceof Multipart) // probably subclass MimeMultipart - { - // We're are not interested in the different parts of the MultipartMessage, - // so we simply read in all data which *can* be huge. - InputStream in = msg.getInputStream(); - this.body = readContent(in); - } - else if(content instanceof InputStream) - { - // The message format is unknown to the Message class, but we can - // simply read in the whole message data. - this.body = readContent((InputStream)content); - } - else - { - // Unknown content is probably a malformed mail we should skip. - // On the other hand we produce an inconsistent mail mirror, but no - // mail system must transport invalid content. - Log.msg("Skipping message due to unknown content. Throwing exception...", true); - throw new MessagingException("Unknown content: " + content); - } - - // Validate headers - validateHeaders(); - } - - /** - * Reads lines from the given InputString into a String object. - * TODO: Move this generalized method to org.sonews.util.io.Resource. - * @param in - * @return - * @throws IOException - */ - private String readContent(InputStream in) - throws IOException - { - StringBuilder buf = new StringBuilder(); - - BufferedReader rin = new BufferedReader(new InputStreamReader(in)); - String line = rin.readLine(); - while(line != null) - { - buf.append('\n'); - buf.append(line); - line = rin.readLine(); - } - - return buf.toString(); - } - - /** - * 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 Database manually to make this - * change persistent. - * Note: a Message-ID should never be changed and only generated once. - */ - private String generateMessageID() - { - String msgID = "<" + UUID.randomUUID() + "@" - + Config.getInstance().get(Config.HOSTNAME, "localhost") + ">"; - - this.headers.setHeader(Headers.MESSAGE_ID, msgID); - - return msgID; - } - - /** - * Returns the body string. - */ - public String getBody() - { - return body; - } - - /** - * @return Charset of the body text - */ - public Charset getBodyCharset() - { - // We espect something like - // Content-Type: text/plain; charset=ISO-8859-15 - String contentType = getHeader(Headers.CONTENT_TYPE)[0]; - int idxCharsetStart = contentType.indexOf("charset=") + "charset=".length(); - int idxCharsetEnd = contentType.indexOf(";", idxCharsetStart); - - String charsetName = "UTF-8"; - if(idxCharsetStart >= 0 && idxCharsetStart < contentType.length()) - { - if(idxCharsetEnd < 0) - { - charsetName = contentType.substring(idxCharsetStart); - } - else - { - charsetName = contentType.substring(idxCharsetStart, idxCharsetEnd); - } - } - - // Sometimes there are '"' around the name - if(charsetName.length() > 2 && - charsetName.charAt(0) == '"' && charsetName.endsWith("\"")) - { - charsetName = charsetName.substring(1, charsetName.length() - 2); - } - - // Create charset - Charset charset = Charset.forName("UTF-8"); // This MUST be supported by JVM - try - { - charset = Charset.forName(charsetName); - } - catch(Exception ex) - { - Log.msg(ex.getMessage(), false); - Log.msg("Article.getBodyCharset(): Unknown charset: " + charsetName, false); - } - return charset; - } - - /** - * @return Numerical IDs of the newsgroups this Article belongs to. - */ - List getGroups() - { - String[] groupnames = getHeader(Headers.NEWSGROUPS)[0].split(","); - ArrayList groups = new ArrayList(); - - try - { - for(String newsgroup : groupnames) - { - newsgroup = newsgroup.trim(); - Group group = Database.getInstance().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 (SQLException ex) - { - ex.printStackTrace(); - return null; - } - return groups; - } - - public void setBody(String body) - { - this.body = body; - } - - /** - * - * @param groupname Name(s) of newsgroups - */ - public void setGroup(String groupname) - { - this.headers.setHeader(Headers.NEWSGROUPS, groupname); - } - - public String getMessageID() - { - String[] msgID = getHeader(Headers.MESSAGE_ID); - return msgID[0]; - } - - public Enumeration getAllHeaders() - { - return this.headers.getAllHeaders(); - } - - /** - * @return Header source code of this Article. - */ - public String getHeaderSource() - { - if(this.headerSrc != null) - { - return this.headerSrc; - } - - StringBuffer buf = new StringBuffer(); - - for(Enumeration en = this.headers.getAllHeaders(); en.hasMoreElements();) - { - Header entry = (Header)en.nextElement(); - - buf.append(entry.getName()); - buf.append(": "); - buf.append( - MimeUtility.fold(entry.getName().length() + 2, entry.getValue())); - - if(en.hasMoreElements()) - { - buf.append("\r\n"); - } - } - - this.headerSrc = buf.toString(); - return this.headerSrc; - } - - public long getIndexInGroup(Group group) - throws SQLException - { - return Database.getInstance().getArticleIndex(this, group); - } - - /** - * Sets the headers of this Article. If headers contain no - * Message-Id a new one is created. - * @param headers - */ - public void setHeaders(InternetHeaders headers) - { - this.headers = headers; - validateHeaders(); - } - - /** - * @return String containing the Message-ID. - */ - @Override - public String toString() - { - return getMessageID(); - } - - /** - * Checks some headers for their validity and generates an - * appropriate Path-header for this host if not yet existing. - * This method is called by some Article constructors and the - * method setHeaders(). - * @return true if something on the headers was changed. - */ - private void validateHeaders() - { - // Check for valid Path-header - final String path = getHeader(Headers.PATH)[0]; - final String host = Config.getInstance().get(Config.HOSTNAME, "localhost"); - if(!path.startsWith(host)) - { - StringBuffer pathBuf = new StringBuffer(); - pathBuf.append(host); - pathBuf.append('!'); - pathBuf.append(path); - this.headers.setHeader(Headers.PATH, pathBuf.toString()); - } - - // Generate a messageID if no one is existing - if(getMessageID().equals("")) - { - generateMessageID(); - } - } - -}