View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.geronimo.javamail.transport.nntp;
21  
22  import java.io.PrintStream;
23  import java.util.ArrayList;
24  
25  import javax.mail.Address;
26  import javax.mail.Message;
27  import javax.mail.MessagingException;
28  import javax.mail.Session;
29  import javax.mail.Transport;
30  import javax.mail.URLName;
31  import javax.mail.event.TransportEvent;
32  import javax.mail.internet.InternetAddress;
33  import javax.mail.internet.MimeMessage;
34  import javax.mail.internet.NewsAddress;
35  
36  import org.apache.geronimo.javamail.util.ProtocolProperties; 
37  
38  /**
39   * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
40   * <p/> There is no way to indicate failure for a given recipient (it's possible
41   * to have a recipient address rejected). The sun impl throws exceptions even if
42   * others successful), but maybe we do a different way... <p/>
43   * 
44   * @version $Rev: 673649 $ $Date: 2008-07-03 06:37:56 -0400 (Thu, 03 Jul 2008) $
45   */
46  public class NNTPTransport extends Transport {
47      /**
48       * property keys for protocol properties.
49       */
50      protected static final String NNTP_FROM = "from";
51  
52      protected static final int DEFAULT_NNTP_PORT = 119;
53      protected static final int DEFAULT_NNTP_SSL_PORT = 563;
54  
55      // our accessor for protocol properties and the holder of 
56      // protocol-specific information 
57      protected ProtocolProperties props; 
58      // our active connection object (shared code with the NNTPStore).
59      protected NNTPConnection connection;
60  
61      /**
62       * Normal constructor for an NNTPTransport() object. This constructor is
63       * used to build a transport instance for the "smtp" protocol.
64       * 
65       * @param session
66       *            The attached session.
67       * @param name
68       *            An optional URLName object containing target information.
69       */
70      public NNTPTransport(Session session, URLName name) {
71          this(session, name, "nntp-post", DEFAULT_NNTP_PORT, false);
72      }
73  
74      /**
75       * Common constructor used by the POP3Store and POP3SSLStore classes
76       * to do common initialization of defaults.
77       *
78       * @param session
79       *            The host session instance.
80       * @param name
81       *            The URLName of the target.
82       * @param protocol
83       *            The protocol type ("pop3"). This helps us in
84       *            retrieving protocol-specific session properties.
85       * @param defaultPort
86       *            The default port used by this protocol. For pop3, this will
87       *            be 110. The default for pop3 with ssl is 995.
88       * @param sslConnection
89       *            Indicates whether an SSL connection should be used to initial
90       *            contact the server. This is different from the STARTTLS
91       *            support, which switches the connection to SSL after the
92       *            initial startup.
93       */
94      protected NNTPTransport(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
95          super(session, name);
96          
97          // create the protocol property holder.  This gives an abstraction over the different 
98          // flavors of the protocol. 
99          props = new ProtocolProperties(session, protocol, sslConnection, defaultPort); 
100         // the connection manages connection for the transport 
101         connection = new NNTPConnection(props); 
102     }
103 
104     /**
105      * Do the protocol connection for an NNTP transport. This handles server
106      * authentication, if possible. Returns false if unable to connect to the
107      * server.
108      * 
109      * @param host
110      *            The target host name.
111      * @param port
112      *            The server port number.
113      * @param user
114      *            The authentication user (if any).
115      * @param password
116      *            The server password. Might not be sent directly if more
117      *            sophisticated authentication is used.
118      * 
119      * @return true if we were able to connect to the server properly, false for
120      *         any failures.
121      * @exception MessagingException
122      */
123     protected boolean protocolConnect(String host, int port, String username, String password)
124             throws MessagingException {
125         // the connection pool handles all of the details here. 
126         return connection.protocolConnect(host, port, username, password);
127     }
128     
129 
130     /**
131      * Send a message to multiple addressees.
132      * 
133      * @param message
134      *            The message we're sending.
135      * @param addresses
136      *            An array of addresses to send to.
137      * 
138      * @exception MessagingException
139      */
140     public void sendMessage(Message message, Address[] addresses) throws MessagingException {
141         if (!isConnected()) {
142             throw new IllegalStateException("Not connected");
143         }
144 
145         if (!connection.isPostingAllowed()) {
146             throw new MessagingException("Posting disabled for host server");
147         }
148         // don't bother me w/ null messages or no addreses
149         if (message == null) {
150             throw new MessagingException("Null message");
151         }
152 
153         // NNTP only handles instances of MimeMessage, not the more general
154         // message case.
155         if (!(message instanceof MimeMessage)) {
156             throw new MessagingException("NNTP can only send MimeMessages");
157         }
158 
159         // need to sort the from value out from a variety of sources.
160         InternetAddress from = null;
161 
162         Address[] fromAddresses = message.getFrom();
163 
164         // If the message has a From address set, we just use that. Otherwise,
165         // we set a From using
166         // the property version, if available.
167         if (fromAddresses == null || fromAddresses.length == 0) {
168             // the from value can be set explicitly as a property
169             String defaultFrom = props.getProperty(NNTP_FROM);
170             if (defaultFrom == null) {
171                 message.setFrom(new InternetAddress(defaultFrom));
172             }
173         }
174 
175         // we must have a message list.
176         if (addresses == null || addresses.length == 0) {
177             throw new MessagingException("Null or empty address array");
178         }
179 
180         boolean haveGroup = false;
181 
182         // enforce the requirement that all of the targets are NewsAddress
183         // instances.
184         for (int i = 0; i < addresses.length; i++) {
185             if (!(addresses[i] instanceof NewsAddress)) {
186                 throw new MessagingException("Illegal NewsAddress " + addresses[i]);
187             }
188         }
189 
190         // event notifcation requires we send lists of successes and failures
191         // broken down by category.
192         // The categories are:
193         //
194         // 1) addresses successfully processed.
195         // 2) addresses deemed valid, but had a processing failure that
196         // prevented sending.
197         // 3) addressed deemed invalid (basically all other processing
198         // failures).
199         ArrayList sentAddresses = new ArrayList();
200         ArrayList unsentAddresses = new ArrayList();
201         ArrayList invalidAddresses = new ArrayList();
202 
203         boolean sendFailure = false;
204 
205         // now try to post this message to the different news groups.
206         for (int i = 0; i < addresses.length; i++) {
207             try {
208                 // select the target news group
209                 NNTPReply reply = connection.selectGroup(((NewsAddress) addresses[i]).getNewsgroup());
210 
211                 if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
212                     invalidAddresses.add(addresses[i]);
213                     sendFailure = true;
214                 } else {
215                     // send data
216                     connection.sendPost(message);
217                     sentAddresses.add(addresses[i]);
218                 }
219             } catch (MessagingException e) {
220                 unsentAddresses.add(addresses[i]);
221                 sendFailure = true;
222             }
223         }
224 
225         // create our lists for notification and exception reporting from this
226         // point on.
227         Address[] sent = (Address[]) sentAddresses.toArray(new Address[0]);
228         Address[] unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
229         Address[] invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
230 
231         if (sendFailure) {
232             // did we deliver anything at all?
233             if (sent.length == 0) {
234                 // notify of the error.
235                 notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
236             } else {
237                 // notify that we delivered at least part of this
238                 notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
239             }
240 
241             throw new MessagingException("Error posting NNTP message");
242         }
243 
244         // notify our listeners of successful delivery.
245         notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
246     }
247 
248     /**
249      * Close the connection. On completion, we'll be disconnected from the
250      * server and unable to send more data.
251      * 
252      * @exception MessagingException
253      */
254     public void close() throws MessagingException {
255         // This is done to ensure proper event notification.
256         super.close();
257         // NB:  We reuse the connection if asked to reconnect 
258         connection.close();
259     }
260 }