1 /**
2 *
3 * Copyright 2003-2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.javamail.transport.nntp;
19
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22
23 import javax.mail.Address;
24 import javax.mail.Message;
25 import javax.mail.MessagingException;
26 import javax.mail.Session;
27 import javax.mail.Transport;
28 import javax.mail.URLName;
29 import javax.mail.event.TransportEvent;
30 import javax.mail.internet.InternetAddress;
31 import javax.mail.internet.MimeMessage;
32 import javax.mail.internet.NewsAddress;
33
34 import org.apache.geronimo.mail.util.SessionUtil;
35
36 /**
37 * Simple implementation of NNTP transport. Just does plain RFC977-ish delivery.
38 * <p/> There is no way to indicate failure for a given recipient (it's possible
39 * to have a recipient address rejected). The sun impl throws exceptions even if
40 * others successful), but maybe we do a different way... <p/>
41 *
42 * @version $Rev: 432884 $ $Date: 2006-08-19 14:53:20 -0700 (Sat, 19 Aug 2006) $
43 */
44 public class NNTPTransport extends Transport {
45
46 /**
47 * property keys for protocol properties.
48 */
49 protected static final String NNTP_AUTH = "auth";
50
51 protected static final String NNTP_PORT = "port";
52
53 protected static final String NNTP_FROM = "from";
54
55 protected static final String protocol = "nntp-post";
56
57 protected static final int DEFAULT_NNTP_PORT = 119;
58
59
60 protected NNTPConnection connection;
61
62
63 protected PrintStream debugStream;
64
65 /**
66 * Normal constructor for an NNTPTransport() object. This constructor is
67 * used to build a transport instance for the "smtp" protocol.
68 *
69 * @param session
70 * The attached session.
71 * @param name
72 * An optional URLName object containing target information.
73 */
74 public NNTPTransport(Session session, URLName name) {
75 super(session, name);
76
77
78 debugStream = session.getDebugOut();
79 }
80
81 /**
82 * Do the protocol connection for an NNTP transport. This handles server
83 * authentication, if possible. Returns false if unable to connect to the
84 * server.
85 *
86 * @param host
87 * The target host name.
88 * @param port
89 * The server port number.
90 * @param user
91 * The authentication user (if any).
92 * @param password
93 * The server password. Might not be sent directly if more
94 * sophisticated authentication is used.
95 *
96 * @return true if we were able to connect to the server properly, false for
97 * any failures.
98 * @exception MessagingException
99 */
100 protected boolean protocolConnect(String host, int port, String username, String password)
101 throws MessagingException {
102 if (debug) {
103 debugOut("Connecting to server " + host + ":" + port + " for user " + username);
104 }
105
106
107
108
109
110 boolean mustAuthenticate = SessionUtil.getBooleanProperty(session, NNTP_AUTH, false);
111
112
113
114
115
116
117 if (mustAuthenticate && (username == null || password == null)) {
118 return false;
119 }
120
121
122
123
124 if (port == -1) {
125
126
127 port = SessionUtil.getIntProperty(session, NNTP_PORT, DEFAULT_NNTP_PORT);
128 }
129
130
131 connection = new NNTPConnection(protocol, session, host, port, username, password, debug);
132 connection.connect();
133
134
135
136
137 return true;
138 }
139
140 /**
141 * Send a message to multiple addressees.
142 *
143 * @param message
144 * The message we're sending.
145 * @param addresses
146 * An array of addresses to send to.
147 *
148 * @exception MessagingException
149 */
150 public void sendMessage(Message message, Address[] addresses) throws MessagingException {
151 if (!isConnected()) {
152 throw new IllegalStateException("Not connected");
153 }
154
155 if (!connection.isPostingAllowed()) {
156 throw new MessagingException("Posting disabled for host server");
157 }
158
159 if (message == null) {
160 throw new MessagingException("Null message");
161 }
162
163
164
165 if (!(message instanceof MimeMessage)) {
166 throw new MessagingException("NNTP can only send MimeMessages");
167 }
168
169
170 InternetAddress from = null;
171
172 Address[] fromAddresses = message.getFrom();
173
174
175
176
177 if (fromAddresses == null || fromAddresses.length == 0) {
178
179 String defaultFrom = session.getProperty(NNTP_FROM);
180 if (defaultFrom == null) {
181 message.setFrom(new InternetAddress(defaultFrom));
182 }
183 }
184
185
186 if (addresses == null || addresses.length == 0) {
187 throw new MessagingException("Null or empty address array");
188 }
189
190 boolean haveGroup = false;
191
192
193
194 for (int i = 0; i < addresses.length; i++) {
195 if (!(addresses[i] instanceof NewsAddress)) {
196 System.out.println("Illegal address is of class " + addresses[i].getClass());
197 throw new MessagingException("Illegal NewsAddress " + addresses[i]);
198 }
199 }
200
201
202
203
204
205
206
207
208
209
210 ArrayList sentAddresses = new ArrayList();
211 ArrayList unsentAddresses = new ArrayList();
212 ArrayList invalidAddresses = new ArrayList();
213
214 boolean sendFailure = false;
215
216
217 for (int i = 0; i < addresses.length; i++) {
218 try {
219
220 NNTPReply reply = connection.selectGroup(((NewsAddress) addresses[i]).getNewsgroup());
221
222 if (reply.getCode() != NNTPReply.GROUP_SELECTED) {
223 invalidAddresses.add(addresses[i]);
224 sendFailure = true;
225 } else {
226
227 connection.sendPost(message);
228 sentAddresses.add(addresses[i]);
229 }
230 } catch (MessagingException e) {
231 unsentAddresses.add(addresses[i]);
232 sendFailure = true;
233 }
234 }
235
236
237
238 Address[] sent = (Address[]) sentAddresses.toArray(new Address[0]);
239 Address[] unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
240 Address[] invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
241
242 if (sendFailure) {
243
244 if (sent.length == 0) {
245
246 notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
247 } else {
248
249 notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
250 }
251
252 throw new MessagingException("Error posting NNTP message");
253 }
254
255
256 notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
257 }
258
259 /**
260 * Close the connection. On completion, we'll be disconnected from the
261 * server and unable to send more data.
262 *
263 * @exception MessagingException
264 */
265 public void close() throws MessagingException {
266
267 super.close();
268 connection.close();
269 connection = null;
270 }
271
272 /**
273 * Internal debug output routine.
274 *
275 * @param value
276 * The string value to output.
277 */
278 protected void debugOut(String message) {
279 debugStream.println("NNTPTransport DEBUG: " + message);
280 }
281
282 /**
283 * Internal debugging routine for reporting exceptions.
284 *
285 * @param message
286 * A message associated with the exception context.
287 * @param e
288 * The received exception.
289 */
290 protected void debugOut(String message, Throwable e) {
291 debugOut("Received exception -> " + message);
292 debugOut("Exception message -> " + e.getMessage());
293 e.printStackTrace(debugStream);
294 }
295
296 /**
297 * Get a property associated with this mail protocol.
298 *
299 * @param name
300 * The name of the property.
301 *
302 * @return The property value (returns null if the property has not been
303 * set).
304 */
305 String getProperty(String name) {
306
307
308
309 String fullName = "mail." + protocol + "." + name;
310 return session.getProperty(fullName);
311 }
312
313 /**
314 * Get a property associated with this mail session. Returns the provided
315 * default if it doesn't exist.
316 *
317 * @param name
318 * The name of the property.
319 * @param defaultValue
320 * The default value to return if the property doesn't exist.
321 *
322 * @return The property value (returns defaultValue if the property has not
323 * been set).
324 */
325 String getProperty(String name, String defaultValue) {
326
327
328
329 String fullName = "mail." + protocol + "." + name;
330 return SessionUtil.getProperty(session, fullName, defaultValue);
331 }
332
333 /**
334 * Get a property associated with this mail session as an integer value.
335 * Returns the default value if the property doesn't exist or it doesn't
336 * have a valid int value.
337 *
338 * @param name
339 * The name of the property.
340 * @param defaultValue
341 * The default value to return if the property doesn't exist.
342 *
343 * @return The property value converted to an int.
344 */
345 int getIntProperty(String name, int defaultValue) {
346
347
348
349 String fullName = "mail." + protocol + "." + name;
350 return SessionUtil.getIntProperty(session, fullName, defaultValue);
351 }
352
353 /**
354 * Get a property associated with this mail session as an boolean value.
355 * Returns the default value if the property doesn't exist or it doesn't
356 * have a valid int value.
357 *
358 * @param name
359 * The name of the property.
360 * @param defaultValue
361 * The default value to return if the property doesn't exist.
362 *
363 * @return The property value converted to a boolean
364 */
365 boolean getBooleanProperty(String name, boolean defaultValue) {
366
367
368
369 String fullName = "mail." + protocol + "." + name;
370 return SessionUtil.getBooleanProperty(session, fullName, defaultValue);
371 }
372 }