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 }