| /* |
| * 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$ $Date$ |
| */ |
| 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); |
| } |
| } |