001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: /*
038: * @(#)POP3Store.java 1.30 07/05/04
039: */
040:
041: package com.sun.mail.pop3;
042:
043: import java.util.Properties;
044: import java.lang.reflect.*;
045:
046: import javax.mail.*;
047: import javax.mail.internet.*;
048: import java.io.IOException;
049: import java.io.EOFException;
050:
051: /**
052: * A POP3 Message Store. Contains only one folder, "INBOX".
053: *
054: * See the <a href="package-summary.html">com.sun.mail.pop3</a> package
055: * documentation for further information on the POP3 protocol provider. <p>
056: *
057: * @author Bill Shannon
058: * @author John Mani
059: */
060: public class POP3Store extends Store {
061:
062: private String name = "pop3"; // my protocol name
063: private int defaultPort = 110; // default POP3 port
064: private boolean isSSL = false; // use SSL?
065:
066: private Protocol port = null; // POP3 port for self
067: private POP3Folder portOwner = null; // folder owning port
068: private String host = null; // host
069: private int portNum = -1;
070: private String user = null;
071: private String passwd = null;
072: boolean rsetBeforeQuit = false;
073: boolean disableTop = false;
074: boolean forgetTopHeaders = false;
075: Constructor messageConstructor = null;
076:
077: public POP3Store(Session session, URLName url) {
078: this (session, url, "pop3", 110, false);
079: }
080:
081: public POP3Store(Session session, URLName url, String name,
082: int defaultPort, boolean isSSL) {
083: super (session, url);
084: if (url != null)
085: name = url.getProtocol();
086: this .name = name;
087: this .defaultPort = defaultPort;
088: this .isSSL = isSSL;
089:
090: String s = session.getProperty("mail." + name
091: + ".rsetbeforequit");
092: if (s != null && s.equalsIgnoreCase("true"))
093: rsetBeforeQuit = true;
094:
095: s = session.getProperty("mail." + name + ".disabletop");
096: if (s != null && s.equalsIgnoreCase("true"))
097: disableTop = true;
098:
099: s = session.getProperty("mail." + name + ".forgettopheaders");
100: if (s != null && s.equalsIgnoreCase("true"))
101: forgetTopHeaders = true;
102:
103: s = session.getProperty("mail." + name + ".message.class");
104: if (s != null) {
105: if (session.getDebug())
106: session.getDebugOut().println(
107: "DEBUG: POP3 message class: " + s);
108: try {
109: ClassLoader cl = this .getClass().getClassLoader();
110:
111: // now load the class
112: Class messageClass = null;
113: try {
114: // First try the "application's" class loader.
115: // This should eventually be replaced by
116: // Thread.currentThread().getContextClassLoader().
117: messageClass = cl.loadClass(s);
118: } catch (ClassNotFoundException ex1) {
119: // That didn't work, now try the "system" class loader.
120: // (Need both of these because JDK 1.1 class loaders
121: // may not delegate to their parent class loader.)
122: messageClass = Class.forName(s);
123: }
124:
125: Class[] c = { javax.mail.Folder.class, int.class };
126: messageConstructor = messageClass.getConstructor(c);
127: } catch (Exception ex) {
128: if (session.getDebug())
129: session.getDebugOut().println(
130: "DEBUG: failed to load POP3 message class: "
131: + ex);
132: }
133: }
134: }
135:
136: protected synchronized boolean protocolConnect(String host,
137: int portNum, String user, String passwd)
138: throws MessagingException {
139:
140: // check for non-null values of host, password, user
141: if (host == null || passwd == null || user == null)
142: return false;
143:
144: // if port is not specified, set it to value of mail.pop3.port
145: // property if it exists, otherwise default to 110
146: if (portNum == -1) {
147: String portstring = session.getProperty("mail." + name
148: + ".port");
149: if (portstring != null)
150: portNum = Integer.parseInt(portstring);
151: }
152:
153: if (portNum == -1)
154: portNum = defaultPort;
155:
156: this .host = host;
157: this .portNum = portNum;
158: this .user = user;
159: this .passwd = passwd;
160: try {
161: port = getPort(null);
162: } catch (EOFException eex) {
163: throw new AuthenticationFailedException(eex.getMessage());
164: } catch (IOException ioex) {
165: throw new MessagingException("Connect failed", ioex);
166: }
167:
168: return true;
169: }
170:
171: /**
172: * Check whether this store is connected. Override superclass
173: * method, to actually ping our server connection. <p>
174: */
175: /*
176: * Note that we maintain somewhat of an illusion of being connected
177: * even if we're not really connected. This is because a Folder
178: * can use the connection and close it when it's done. If we then
179: * ask whether the Store's connected we want the answer to be true,
180: * as long as we can reconnect at that point. This means that we
181: * need to be able to reconnect the Store on demand.
182: */
183: public synchronized boolean isConnected() {
184: if (!super .isConnected())
185: // if we haven't been connected at all, don't bother with
186: // the NOOP.
187: return false;
188: synchronized (this ) {
189: try {
190: if (port == null)
191: port = getPort(null);
192: else
193: port.noop();
194: return true;
195: } catch (IOException ioex) {
196: // no longer connected, close it down
197: try {
198: super .close(); // notifies listeners
199: } catch (MessagingException mex) {
200: // ignore it
201: } finally {
202: return false;
203: }
204: }
205: }
206: }
207:
208: synchronized Protocol getPort(POP3Folder owner) throws IOException {
209: Protocol p;
210:
211: // if we already have a port, remember who's using it
212: if (port != null && portOwner == null) {
213: portOwner = owner;
214: return port;
215: }
216:
217: // need a new port, create it and try to login
218: p = new Protocol(host, portNum, session.getDebug(), session
219: .getDebugOut(), session.getProperties(),
220: "mail." + name, isSSL);
221:
222: String msg = null;
223: if ((msg = p.login(user, passwd)) != null) {
224: try {
225: p.quit();
226: } catch (IOException ioex) {
227: } finally {
228: throw new EOFException(msg);
229: }
230: }
231:
232: /*
233: * If a Folder closes the port, and then a Folder
234: * is opened, the Store won't have a port. In that
235: * case, the getPort call will come from Folder.open,
236: * but we need to keep track of the port in the Store
237: * so that a later call to Folder.isOpen, which calls
238: * Store.isConnected, will use the same port.
239: */
240: if (port == null && owner != null) {
241: port = p;
242: portOwner = owner;
243: }
244: if (portOwner == null)
245: portOwner = owner;
246: return p;
247: }
248:
249: synchronized void closePort(POP3Folder owner) {
250: if (portOwner == owner) {
251: port = null;
252: portOwner = null;
253: }
254: }
255:
256: public synchronized void close() throws MessagingException {
257: try {
258: if (port != null)
259: port.quit();
260: } catch (IOException ioex) {
261: } finally {
262: port = null;
263:
264: // to set the state and send the closed connection event
265: super .close();
266: }
267: }
268:
269: public Folder getDefaultFolder() throws MessagingException {
270: checkConnected();
271: return new DefaultFolder(this );
272: }
273:
274: /**
275: * Only the name "INBOX" is supported.
276: */
277: public Folder getFolder(String name) throws MessagingException {
278: checkConnected();
279: return new POP3Folder(this , name);
280: }
281:
282: public Folder getFolder(URLName url) throws MessagingException {
283: checkConnected();
284: return new POP3Folder(this , url.getFile());
285: }
286:
287: protected void finalize() throws Throwable {
288: super .finalize();
289:
290: if (port != null) // don't force a connection attempt
291: close();
292: }
293:
294: private void checkConnected() throws MessagingException {
295: if (!super .isConnected())
296: throw new MessagingException("Not connected");
297: }
298: }
|