All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geronimo.javamail.store.nntp.NNTPMessage Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.geronimo.javamail.store.nntp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;

import javax.mail.Flags;
import javax.mail.IllegalWriteException;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeMessage;

import org.apache.geronimo.javamail.transport.nntp.NNTPConnection;
import org.apache.geronimo.javamail.transport.nntp.NNTPReply;
import org.apache.geronimo.javamail.transport.nntp.StringListInputStream;

/**
 * NNTP implementation of javax.mail.internet.MimeMessage
 * 
 * Only the most basic information is given and Message objects created here is
 * a light-weight reference to the actual Message As per the JavaMail spec items
 * from the actual message will get filled up on demand
 * 
 * If some other items are obtained from the server as a result of one call,
 * then the other details are also processed and filled in. For ex if RETR is
 * called then header information will also be processed in addition to the
 * content
 * 
 * @version $Rev: 437941 $ $Date: 2006-08-28 23:56:02 -0400 (Mon, 28 Aug 2006) $
 */
public class NNTPMessage extends MimeMessage {
    // the server message identifer
    String messageID = null;

    // our attached session
    protected Session session;

    // the Store we're stored in (which manages the connection and other stuff).
    protected NNTPStore store;

    // our active connection.
    protected NNTPConnection connection;

    // used to force loading of headers
    protected boolean headersLoaded = false;

    // use to force content loading
    protected boolean contentLoaded = false;

    /**
     * Contruct an NNTPMessage instance.
     * 
     * @param folder
     *            The hosting folder for the message.
     * @param store
     *            The Store owning the article (and folder).
     * @param msgnum
     *            The article message number.
     * @param messageID
     *            The article messageID (as assigned by the server).
     * 
     * @exception MessagingException
     */
    NNTPMessage(NNTPFolder folder, NNTPStore store, int msgnum, String messageID) throws MessagingException {
        super(folder, msgnum);
        this.messageID = messageID;
        this.store = store;
        this.session = ((NNTPStore) store).getSession();
        // get the active connection from the store...all commands are sent
        // there
        this.connection = ((NNTPStore) store).getConnection();

        // get our flag set from the folder.
        flags = folder.getPermanentFlags();
        // now check our initial SEEN state and set the flags appropriately
        if (folder.isSeen(msgnum)) {
            flags.add(Flags.Flag.SEEN);
        } else {
            flags.remove(Flags.Flag.SEEN);
        }
    }

    /**
     * Retrieve the size of the message content. The content will be retrieved
     * from the server, if necessary.
     * 
     * @return The size of the content.
     * @exception MessagingException
     */
    public int getSize() throws MessagingException {
        // make sure we've retrieved the message content and continue with the
        // superclass version.
        loadContent();
        return super.getSize();
    }

    /**
     * Get a line count for the NNTP message. This is potentially stored in the
     * Lines article header. If not there, we return a default of -1.
     * 
     * @return The header line count estimate, or -1 if not retrieveable.
     * @exception MessagingException
     */
    public int getLineCount() throws MessagingException {
        String[] headers = getHeader("Lines");

        // hopefully, there's only a single one of these. No sensible way of
        // interpreting
        // multiples.
        if (headers.length == 1) {
            try {
                return Integer.parseInt(headers[0].trim());

            } catch (NumberFormatException e) {
                // ignore
            }
        }
        // dunno...and let them know I don't know.
        return -1;
    }

    /**
     * @see javax.mail.internet.MimeMessage#getContentStream()
     */
    protected InputStream getContentStream() throws MessagingException {
        // get the article information.
        loadArticle();
        return super.getContentStream();
    }

    /***************************************************************************
     * Following is a set of methods that deal with headers These methods are
     * just overrides on the superclass methods to allow lazy loading of the
     * header information.
     **************************************************************************/

    public String[] getHeader(String name) throws MessagingException {
        loadHeaders();
        return headers.getHeader(name);
    }

    public String getHeader(String name, String delimiter) throws MessagingException {
        loadHeaders();
        return headers.getHeader(name, delimiter);
    }

    public Enumeration getAllHeaders() throws MessagingException {
        loadHeaders();
        return headers.getAllHeaders();
    }

    public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
        loadHeaders();
        return headers.getMatchingHeaders(names);
    }

    public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
        loadHeaders();
        return headers.getNonMatchingHeaders(names);
    }

    public Enumeration getAllHeaderLines() throws MessagingException {
        loadHeaders();
        return headers.getAllHeaderLines();
    }

    public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
        loadHeaders();
        return headers.getMatchingHeaderLines(names);
    }

    public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
        loadHeaders();
        return headers.getNonMatchingHeaderLines(names);
    }

    // the following are overrides for header modification methods. These
    // messages are read only,
    // so the headers cannot be modified.
    public void addHeader(String name, String value) throws MessagingException {
        throw new IllegalWriteException("NNTP messages are read-only");
    }

    public void setHeader(String name, String value) throws MessagingException {
        throw new IllegalWriteException("NNTP messages are read-only");
    }

    public void removeHeader(String name) throws MessagingException {
        throw new IllegalWriteException("NNTP messages are read-only");
    }

    public void addHeaderLine(String line) throws MessagingException {
        throw new IllegalWriteException("IMAP messages are read-only");
    }

    /**
     * We cannot modify these messages
     */
    public void saveChanges() throws MessagingException {
        throw new IllegalWriteException("NNTP messages are read-only");
    }

    /**
     * Retrieve the message headers from the NNTP server.
     * 
     * @exception MessagingException
     */
    public void loadHeaders() throws MessagingException {
        // don't retrieve if already loaded.
        if (headersLoaded) {
            return;
        }

        NNTPReply reply = connection.sendCommand("HEAD " + messageID, NNTPReply.HEAD_FOLLOWS);

        if (reply.getCode() == NNTPReply.HEAD_FOLLOWS) {
            try {
                // wrap a stream around the reply data and read as headers.
                updateHeaders(new StringListInputStream(reply.getData()));
            } catch (IOException e) {
                throw new MessagingException("Error retrieving article headers from server", e);
            }
        } else {
            throw new MessagingException("Error retrieving article headers from server: " + reply);
        }
    }

    /**
     * Update the message headers from an input stream.
     * 
     * @param in
     *            The InputStream source for the header information.
     * 
     * @exception MessagingException
     */
    public void updateHeaders(InputStream in) throws MessagingException {
        // wrap a stream around the reply data and read as headers.
        headers = new InternetHeaders(in);
        headersLoaded = true;
    }

    /**
     * Load just the message content from the NNTP server.
     * 
     * @exception MessagingException
     */
    public void loadContent() throws MessagingException {
        if (contentLoaded) {
            return;
        }

        NNTPReply reply = connection.sendCommand("BODY " + messageID, NNTPReply.BODY_FOLLOWS);

        if (reply.getCode() == NNTPReply.BODY_FOLLOWS) {
            try {
                InputStream in = new StringListInputStream(reply.getData());
                updateContent(in);
            } catch (IOException e) {
                throw new MessagingException("Error retrieving article body from server", e);
            }
        } else {
            throw new MessagingException("Error retrieving article body from server: " + reply);
        }
    }

    /**
     * Load the entire article from the NNTP server. This updates both the
     * headers and the content.
     * 
     * @exception MessagingException
     */
    public void loadArticle() throws MessagingException {
        // if the headers are already loaded, retrieve the content portion.
        if (headersLoaded) {
            loadContent();
            return;
        }

        // we need to retrieve everything.
        NNTPReply reply = connection.sendCommand("ARTICLE " + messageID, NNTPReply.ARTICLE_FOLLOWS);

        if (reply.getCode() == NNTPReply.ARTICLE_FOLLOWS) {
            try {
                InputStream in = new StringListInputStream(reply.getData());
                // update both the headers and the content.
                updateHeaders(in);
                updateContent(in);
            } catch (IOException e) {
                throw new MessagingException("Error retrieving article from server", e);
            }
        } else {
            throw new MessagingException("Error retrieving article from server: " + reply);
        }
    }

    /**
     * Update the article content from an input stream.
     * 
     * @param in
     *            The content data source.
     * 
     * @exception MessagingException
     */
    public void updateContent(InputStream in) throws MessagingException {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            byte[] buffer = new byte[4096];

            // copy the content data from the stream into a byte buffer for the
            // content.
            while (true) {
                int read = in.read(buffer);
                if (read == -1) {
                    break;
                }
                out.write(buffer, 0, read);
            }

            content = out.toByteArray();
            contentLoaded = true;
        } catch (IOException e) {
            throw new MessagingException("Error retrieving message body from server", e);
        }
    }

    /**
     * Get the server assigned messageid for the article.
     * 
     * @return The server assigned message id.
     */
    public String getMessageId() {
        return messageID;
    }

    /**
     * Override of setFlags(). We need to ensure that if the SEEN flag is set or
     * cleared, that the newsrc file correctly reflects the current state.
     * 
     * @param flag
     *            The flag being set.
     * @param newvalue
     *            The new flag value.
     * 
     * @exception MessagingException
     */
    public void setFlags(Flags flag, boolean newvalue) throws MessagingException {
        // if this is the SEEN flag, make sure we shadow this in the newsrc
        // file.
        if (flag.contains(Flags.Flag.SEEN)) {
            ((NNTPFolder) folder).setSeen(msgnum, newvalue);
        }
        // have the superclass do the real flag setting.
        super.setFlags(flag, newvalue);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy