001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.geronimo.javamail.transport.nntp;
021
022 import java.io.PrintStream;
023 import java.util.ArrayList;
024
025 import javax.mail.Address;
026 import javax.mail.Message;
027 import javax.mail.MessagingException;
028 import javax.mail.Session;
029 import javax.mail.Transport;
030 import javax.mail.URLName;
031 import javax.mail.event.TransportEvent;
032 import javax.mail.internet.InternetAddress;
033 import javax.mail.internet.MimeMessage;
034 import javax.mail.internet.NewsAddress;
035
036 import org.apache.geronimo.javamail.util.ProtocolProperties;
037
038 /**
039 * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
040 * <p/> There is no way to indicate failure for a given recipient (it's possible
041 * to have a recipient address rejected). The sun impl throws exceptions even if
042 * others successful), but maybe we do a different way... <p/>
043 *
044 * @version $Rev: 673649 $ $Date: 2008-07-03 06:37:56 -0400 (Thu, 03 Jul 2008) $
045 */
046 public class NNTPTransport extends Transport {
047 /**
048 * property keys for protocol properties.
049 */
050 protected static final String NNTP_FROM = "from";
051
052 protected static final int DEFAULT_NNTP_PORT = 119;
053 protected static final int DEFAULT_NNTP_SSL_PORT = 563;
054
055 // our accessor for protocol properties and the holder of
056 // protocol-specific information
057 protected ProtocolProperties props;
058 // our active connection object (shared code with the NNTPStore).
059 protected NNTPConnection connection;
060
061 /**
062 * Normal constructor for an NNTPTransport() object. This constructor is
063 * used to build a transport instance for the "smtp" protocol.
064 *
065 * @param session
066 * The attached session.
067 * @param name
068 * An optional URLName object containing target information.
069 */
070 public NNTPTransport(Session session, URLName name) {
071 this(session, name, "nntp-post", DEFAULT_NNTP_PORT, false);
072 }
073
074 /**
075 * Common constructor used by the POP3Store and POP3SSLStore classes
076 * to do common initialization of defaults.
077 *
078 * @param session
079 * The host session instance.
080 * @param name
081 * The URLName of the target.
082 * @param protocol
083 * The protocol type ("pop3"). This helps us in
084 * retrieving protocol-specific session properties.
085 * @param defaultPort
086 * The default port used by this protocol. For pop3, this will
087 * be 110. The default for pop3 with ssl is 995.
088 * @param sslConnection
089 * Indicates whether an SSL connection should be used to initial
090 * contact the server. This is different from the STARTTLS
091 * support, which switches the connection to SSL after the
092 * initial startup.
093 */
094 protected NNTPTransport(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
095 super(session, name);
096
097 // create the protocol property holder. This gives an abstraction over the different
098 // flavors of the protocol.
099 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 }