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 }