geronimo-javamail-1.4-spec-1.7.1/0000775000175000017500000000000011703375731015332 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/pom.xml0000664000175000017500000001532111404411025016632 0ustar brianbrian 4.0.0 org.apache.geronimo.genesis genesis-java5-flava 2.0 org.apache.geronimo.specs geronimo-javamail_1.4_spec bundle JavaMail 1.4 1.7.1 Javamail 1.4 Specification http://geronimo.apache.org/maven/${siteId}/${version} apache-website ${site.deploy.url}/maven/${siteId}/${version} specs/${artifactId} org.apache.geronimo.mail* src/main/resources/,META-INF/LICENSE.txt=LICENSE.txt,META-INF/NOTICE.txt=NOTICE.txt scm:svn:https://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.7.1 scm:svn:https://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.7.1 https://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.7.1 org.apache.geronimo.specs geronimo-activation_1.1_spec 1.1 provided org.osgi org.osgi.core 4.2.0 provided org.osgi org.osgi.compendium 4.2.0 provided org.apache.geronimo.specs geronimo-osgi-locator 1.0 provided junit junit 3.8.2 test org.apache.felix maven-bundle-plugin ${groupId}.${artifactId};singleton=true JSR-919 Javamail API 1.4 Sun Microsystems, Inc. 1.4 javax.mail*;version=1.4,org.apache.geronimo.mail.util;version=1.4,org.apache.geronimo.mail.handlers;version=1.4 org.apache.geronimo.osgi.registry.api;resolution:=optional,* org.apache.geronimo.osgi.locator,org.apache.geronimo.mail org.apache.geronimo.mail.Activator rat org.apache.rat apache-rat-plugin 0.6 verify check ${project.build.directory}/${project.build.finalName}.rat false **/target/**/* **/appended-resources/**/* **/velocity.log **/*.MF **/wmtom.bin manual/src/styles/print.css geronimo-javamail-1.4-spec-1.7.1/src/0000775000175000017500000000000011703375727016126 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/0000775000175000017500000000000011703375731017045 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/resources/0000775000175000017500000000000011703375731021057 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/resources/META-INF/0000775000175000017500000000000011703375731022217 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/resources/META-INF/default.address.map0000664000175000017500000000166310517560657026001 0ustar brianbrian## ## Licensed to the Apache Software Foundation (ASF) under one ## or more contributor license agreements. See the NOTICE file ## distributed with this work for additional information ## regarding copyright ownership. The ASF licenses this file ## to you under the Apache License, Version 2.0 (the ## "License"); you may not use this file except in compliance ## with the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, ## software distributed under the License is distributed on an ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ## KIND, either express or implied. See the License for the ## specific language governing permissions and limitations ## under the License. ## ## ## $Rev: 438769 $ $Date: 2006-08-30 21:03:44 -0700 (Wed, 30 Aug 2006) $ ## # only the single mapping for smtp is defined. rfc822=smtp geronimo-javamail-1.4-spec-1.7.1/src/main/resources/META-INF/mailcap0000664000175000017500000000247110517560657023561 0ustar brianbrian## ## Licensed to the Apache Software Foundation (ASF) under one ## or more contributor license agreements. See the NOTICE file ## distributed with this work for additional information ## regarding copyright ownership. The ASF licenses this file ## to you under the Apache License, Version 2.0 (the ## "License"); you may not use this file except in compliance ## with the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, ## software distributed under the License is distributed on an ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ## KIND, either express or implied. See the License for the ## specific language governing permissions and limitations ## under the License. ## ## ## $Rev: 438769 $ $Date: 2006-08-30 21:03:44 -0700 (Wed, 30 Aug 2006) $ ## text/plain;; x-java-content-handler=org.apache.geronimo.mail.handlers.TextHandler text/xml;; x-java-content-handler=org.apache.geronimo.mail.handlers.XMLHandler text/html;; x-java-content-handler=org.apache.geronimo.mail.handlers.HtmlHandler message/rfc822;; x-java-content-handler=org.apache.geronimo.mail.handlers.MessageHandler multipart/*;; x-java-content-handler=org.apache.geronimo.mail.handlers.MultipartHandler; x-java-fallback-entry=true geronimo-javamail-1.4-spec-1.7.1/src/main/resources/META-INF/javamail.charset.map0000664000175000017500000000410211265062127026122 0ustar brianbrian## ## Licensed to the Apache Software Foundation (ASF) under one ## or more contributor license agreements. See the NOTICE file ## distributed with this work for additional information ## regarding copyright ownership. The ASF licenses this file ## to you under the Apache License, Version 2.0 (the ## "License"); you may not use this file except in compliance ## with the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, ## software distributed under the License is distributed on an ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ## KIND, either express or implied. See the License for the ## specific language governing permissions and limitations ## under the License. ## ## ## $Rev: 438769 $ $Date: 2006-08-30 21:03:44 -0700 (Wed, 30 Aug 2006) $ ## ### Character set mapping table loaded and used by the javax.mail.internet.MimeUtility class. ### java character sets to MIME character set map. This must be the first table. 8859_1 ISO-8859-1 iso8859_1 ISO-8859-1 ISO8859_1 ISO-8859-1 8859_2 ISO-8859-2 iso8859_2 ISO-8859-2 ISO8859_2 ISO-8859-2 8859_3 ISO-8859-3 iso8859_3 ISO-8859-3 ISO8859_3 ISO-8859-3 8859_4 ISO-8859-4 iso8859_4 ISO-8859-4 ISO8859_4 ISO-8859-4 8859_5 ISO-8859-5 iso8859_5 ISO-8859-5 ISO8859_5 ISO-8859-5 8859_6 ISO-8859-6 iso8859_6 ISO-8859-6 ISO8859_6 ISO-8859-6 8859_7 ISO-8859-7 iso8859_7 ISO-8859-7 ISO8859_7 ISO-8859-7 8859_8 ISO-8859-8 iso8859_8 ISO-8859-8 ISO8859_8 ISO-8859-8 8859_9 ISO-8859-9 iso8859_9 ISO-8859-9 ISO8859_9 ISO-8859-9 SJIS Shift_JIS MS932 Shift_JIS JIS ISO-2022-JP ISO2022JP ISO-2022-JP EUC_JP euc-jp KOI8_R koi8-r EUC_CN euc-cn EUC_TW euc-tw EUC_KR euc-kr --Table terminator. The "--" at the beginning and end are required -- #### MIME to java character set map iso-2022-cn ISO2022CN iso-2022-kr ISO2022KR utf-8 UTF8 utf8 UTF8 ja_jp.iso2022-7 ISO2022JP ja_jp.eucjp EUCJIS euc-kr KSC5601 euckr KSC5601 us-ascii ISO-8859-1 x-us-ascii ISO-8859-1 geronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/0000775000175000017500000000000011703375731022232 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/0000775000175000017500000000000011703375731024247 5ustar brianbrian././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.HtmlHandlergeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.Html0000664000175000017500000000160711361343462031624 0ustar brianbrian# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. org.apache.geronimo.mail.handlers.HtmlHandler # This is directly mapped back to the same class name ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.TextHandlergeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.Text0000664000175000017500000000160711361343462031644 0ustar brianbrian# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. org.apache.geronimo.mail.handlers.TextHandler # This is directly mapped back to the same class name ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.MultipartHandlergeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.Mult0000664000175000017500000000161211361343462031635 0ustar brianbrian# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. org.apache.geronimo.mail.handlers.MessageHandler # This is directly mapped back to the same class name ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.XMLHandlergeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.XMLH0000664000175000017500000000160611361343462031467 0ustar brianbrian# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. org.apache.geronimo.mail.handlers.XMLHandler # This is directly mapped back to the same class name ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.MessageHandlergeronimo-javamail-1.4-spec-1.7.1/src/main/resources/OSGI-INF/providers/org.apache.mail.handlers.Mess0000664000175000017500000000161211361343462031623 0ustar brianbrian# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. org.apache.geronimo.mail.handlers.MessageHandler # This is directly mapped back to the same class name geronimo-javamail-1.4-spec-1.7.1/src/main/java/0000775000175000017500000000000011703375730017765 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/0000775000175000017500000000000011703375730021076 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/0000775000175000017500000000000011703375731022021 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Multipart.java0000664000175000017500000001073210517560657024655 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.IOException; import java.io.OutputStream; import java.util.Vector; /** * A container for multiple {@link BodyPart BodyParts}. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class Multipart { /** * Vector of sub-parts. */ protected Vector parts = new Vector(); /** * The content type of this multipart object; defaults to "multipart/mixed" */ protected String contentType = "multipart/mixed"; /** * The Part that contains this multipart. */ protected Part parent; protected Multipart() { } /** * Initialize this multipart object from the supplied data source. * This adds any {@link BodyPart BodyParts} into this object and initializes the content type. * * @param mds the data source * @throws MessagingException */ protected void setMultipartDataSource(MultipartDataSource mds) throws MessagingException { parts.clear(); contentType = mds.getContentType(); int size = mds.getCount(); for (int i = 0; i < size; i++) { parts.add(mds.getBodyPart(i)); } } /** * Return the content type. * * @return the content type */ public String getContentType() { return contentType; } /** * Return the number of enclosed parts * * @return the number of parts * @throws MessagingException */ public int getCount() throws MessagingException { return parts.size(); } /** * Get the specified part; numbering starts at zero. * * @param index the part to get * @return the part * @throws MessagingException */ public BodyPart getBodyPart(int index) throws MessagingException { return (BodyPart) parts.get(index); } /** * Remove the supplied part from the list. * * @param part the part to remove * @return true if the part was removed * @throws MessagingException */ public boolean removeBodyPart(BodyPart part) throws MessagingException { return parts.remove(part); } /** * Remove the specified part; all others move down one * * @param index the part to remove * @throws MessagingException */ public void removeBodyPart(int index) throws MessagingException { parts.remove(index); } /** * Add a part to the end of the list. * * @param part the part to add * @throws MessagingException */ public void addBodyPart(BodyPart part) throws MessagingException { parts.add(part); } /** * Insert a part into the list at a designated point; all subsequent parts move down * * @param part the part to add * @param pos the index of the new part * @throws MessagingException */ public void addBodyPart(BodyPart part, int pos) throws MessagingException { parts.add(pos, part); } /** * Encode and write this multipart to the supplied OutputStream; the encoding * used is determined by the implementation. * * @param out the stream to write to * @throws IOException * @throws MessagingException */ public abstract void writeTo(OutputStream out) throws IOException, MessagingException; /** * Return the Part containing this Multipart object or null if unknown. * * @return this Multipart's parent */ public Part getParent() { return parent; } /** * Set the parent of this Multipart object * * @param part this object's parent */ public void setParent(Part part) { parent = part; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Service.java0000664000175000017500000004112411267033717024266 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.Vector; import javax.mail.event.ConnectionEvent; import javax.mail.event.ConnectionListener; import javax.mail.event.MailEvent; /** * @version $Rev: 826623 $ $Date: 2009-10-19 05:56:31 -0400 (Mon, 19 Oct 2009) $ */ public abstract class Service { /** * The session from which this service was created. */ protected Session session; /** * The URLName of this service */ protected URLName url; /** * Debug flag for this service, set from the Session's debug flag. */ protected boolean debug; private boolean connected; private final Vector connectionListeners = new Vector(2); // the EventQueue spins off a new thread, so we only create this // if we have actual listeners to dispatch an event to. private EventQueue queue = null; // when returning the URL, we need to ensure that the password and file information is // stripped out. private URLName exposedUrl; /** * Construct a new Service. * @param session the session from which this service was created * @param url the URLName of this service */ protected Service(Session session, URLName url) { this.session = session; this.url = url; this.debug = session.getDebug(); } /** * A generic connect method that takes no parameters allowing subclasses * to implement an appropriate authentication scheme. * The default implementation calls connect(null, null, null) * @throws AuthenticationFailedException if authentication fails * @throws MessagingException for other failures */ public void connect() throws MessagingException { connect(null, null, null); } /** * Connect to the specified host using a simple username/password authenticaion scheme * and the default port. * The default implementation calls connect(host, -1, user, password) * * @param host the host to connect to * @param user the user name * @param password the user's password * @throws AuthenticationFailedException if authentication fails * @throws MessagingException for other failures */ public void connect(String host, String user, String password) throws MessagingException { connect(host, -1, user, password); } /** * Connect to the specified host using a simple username/password authenticaion scheme * and the default host and port. * The default implementation calls connect(host, -1, user, password) * * @param user the user name * @param password the user's password * @throws AuthenticationFailedException if authentication fails * @throws MessagingException for other failures */ public void connect(String user, String password) throws MessagingException { connect(null, -1, user, password); } /** * Connect to the specified host at the specified port using a simple username/password authenticaion scheme. * * If this Service is already connected, an IllegalStateException is thrown. * * @param host the host to connect to * @param port the port to connect to; pass -1 to use the default for the protocol * @param user the user name * @param password the user's password * @throws AuthenticationFailedException if authentication fails * @throws MessagingException for other failures * @throws IllegalStateException if this service is already connected */ public void connect(String host, int port, String user, String password) throws MessagingException { if (isConnected()) { throw new IllegalStateException("Already connected"); } // before we try to connect, we need to derive values for some parameters that may not have // been explicitly specified. For example, the normal connect() method leaves us to derive all // of these from other sources. Some of the values are derived from our URLName value, others // from session parameters. We need to go through all of these to develop a set of values we // can connect with. // this is the protocol we're connecting with. We use this largely to derive configured values from // session properties. String protocol = null; // if we're working with the URL form, then we can retrieve the protocol from the URL. if (url != null) { protocol = url.getProtocol(); } // if the port is -1, see if we have an override from url. if (port == -1) { if (protocol != null) { port = url.getPort(); } } // now try to derive values for any of the arguments we've been given as defaults if (host == null) { // first choice is from the url, if we have if (url != null) { host = url.getHost(); // it is possible that this could return null (rare). If it does, try to get a // value from a protocol specific session variable. if (host == null) { if (protocol != null) { host = session.getProperty("mail." + protocol + ".host"); } } } // this may still be null...get the global mail property if (host == null) { host = session.getProperty("mail.host"); } } // ok, go after userid information next. if (user == null) { // first choice is from the url, if we have if (url != null) { user = url.getUsername(); // make sure we get the password from the url, if we can. if (password == null) { password = url.getPassword(); } } // user still null? We have several levels of properties to try yet if (user == null) { if (protocol != null) { user = session.getProperty("mail." + protocol + ".user"); } // this may still be null...get the global mail property if (user == null) { user = session.getProperty("mail.user"); // still null, try using the user.name system property if (user == null) { // finally, we try getting the system defined user name try { user = System.getProperty("user.name"); } catch (SecurityException e) { // we ignore this, and just us a null username. } } } } } // if we have an explicitly given user name, we need to see if this matches the url one and // grab the password from there. else { if (url != null && user.equals(url.getUsername())) { password = url.getPassword(); } } // we need to update the URLName associated with this connection once we have all of the information, // which means we also need to propogate the file portion of the URLName if we have this form when // we start. String file = null; if (url != null) { file = url.getFile(); } // see if we have cached security information to use. If this is not cached, we'll save it // after we successfully connect. boolean cachePassword = false; // still have a null password to this point, and using a url form? if (password == null && url != null) { // construct a new URL, filling in any pieces that may have been explicitly specified. setURLName(new URLName(protocol, host, port, file, user, password)); // now see if we have a saved password from a previous request. PasswordAuthentication cachedPassword = session.getPasswordAuthentication(getURLName()); // if we found a saved one, see if we need to get any the pieces from here. if (cachedPassword != null) { // not even a resolved userid? Then use both bits. if (user == null) { user = cachedPassword.getUserName(); password = cachedPassword.getPassword(); } // our user name must match the cached name to be valid. else if (user.equals(cachedPassword.getUserName())) { password = cachedPassword.getPassword(); } } else { // nothing found in the cache, so we need to save this if we can connect successfully. cachePassword = true; } } // we've done our best up to this point to obtain all of the information needed to make the // connection. Now we pass this off to the protocol handler to see if it works. If we get a // connection failure, we may need to prompt for a password before continuing. try { connected = protocolConnect(host, port, user, password); } catch (AuthenticationFailedException e) { } if (!connected) { InetAddress ipAddress = null; try { ipAddress = InetAddress.getByName(host); } catch (UnknownHostException e) { } // now ask the session to try prompting for a password. PasswordAuthentication promptPassword = session.requestPasswordAuthentication(ipAddress, port, protocol, null, user); // if we were able to obtain new information from the session, then try again using the // provided information . if (promptPassword != null) { user = promptPassword.getUserName(); password = promptPassword.getPassword(); } connected = protocolConnect(host, port, user, password); } // if we're still not connected, then this is an exception. if (!connected) { throw new AuthenticationFailedException(); } // the URL name needs to reflect the most recent information. setURLName(new URLName(protocol, host, port, file, user, password)); // we need to update the global password cache with this information. if (cachePassword) { session.setPasswordAuthentication(getURLName(), new PasswordAuthentication(user, password)); } // we're now connected....broadcast this to any interested parties. setConnected(connected); notifyConnectionListeners(ConnectionEvent.OPENED); } /** * Attempt the protocol-specific connection; subclasses should override this to establish * a connection in the appropriate manner. * * This method should return true if the connection was established. * It may return false to cause the {@link #connect(String, int, String, String)} method to * reattempt the connection after trying to obtain user and password information from the user. * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt. * * @param host The target host name of the service. * @param port The connection port for the service. * @param user The user name used for the connection. * @param password The password used for the connection. * * @return true if a connection was established, false if there was authentication * error with the connection. * @throws AuthenticationFailedException * if authentication fails * @throws MessagingException * for other failures */ protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException { return false; } /** * Check if this service is currently connected. * The default implementation simply returns the value of a private boolean field; * subclasses may wish to override this method to verify the physical connection. * * @return true if this service is connected */ public boolean isConnected() { return connected; } /** * Notification to subclasses that the connection state has changed. * This method is called by the connect() and close() methods to indicate state change; * subclasses should also call this method if the connection is automatically closed * for some reason. * * @param connected the connection state */ protected void setConnected(boolean connected) { this.connected = connected; } /** * Close this service and terminate its physical connection. * The default implementation simply calls setConnected(false) and then * sends a CLOSED event to all registered ConnectionListeners. * Subclasses overriding this method should still ensure it is closed; they should * also ensure that it is called if the connection is closed automatically, for * for example in a finalizer. * *@throws MessagingException if there were errors closing; the connection is still closed */ public void close() throws MessagingException { setConnected(false); notifyConnectionListeners(ConnectionEvent.CLOSED); } /** * Return a copy of the URLName representing this service with the password and file information removed. * * @return the URLName for this service */ public URLName getURLName() { // if we haven't composed the URL version we hand out, create it now. But only if we really // have a URL. if (exposedUrl == null) { if (url != null) { exposedUrl = new URLName(url.getProtocol(), url.getHost(), url.getPort(), null, url.getUsername(), null); } } return exposedUrl; } /** * Set the url field. * @param url the new value */ protected void setURLName(URLName url) { this.url = url; } public void addConnectionListener(ConnectionListener listener) { connectionListeners.add(listener); } public void removeConnectionListener(ConnectionListener listener) { connectionListeners.remove(listener); } protected void notifyConnectionListeners(int type) { queueEvent(new ConnectionEvent(this, type), connectionListeners); } public String toString() { // NOTE: We call getURLName() rather than use the URL directly // because the get method strips out the password information. URLName url = getURLName(); return url == null ? super.toString() : url.toString(); } protected void queueEvent(MailEvent event, Vector listeners) { // if there are no listeners to dispatch this to, don't put it on the queue. // This allows us to delay creating the queue (and its new thread) until // we if (listeners.isEmpty()) { return; } // first real event? Time to get the queue kicked off. if (queue == null) { queue = new EventQueue(); } // tee it up and let it rip. queue.queueEvent(event, (List)listeners.clone()); } protected void finalize() throws Throwable { // stop our event queue if we had to create one if (queue != null) { queue.stop(); } connectionListeners.clear(); super.finalize(); } /** * Package scope utility method to allow Message instances * access to the Service's session. * * @return The Session the service is associated with. */ Session getSession() { return session; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/QuotaAwareStore.java0000664000175000017500000000326110700432002025731 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * An interface for Store implementations to support the IMAP RFC 2087 Quota extension. * * @version $Rev: 581202 $ $Date: 2007-10-02 08:05:22 -0400 (Tue, 02 Oct 2007) $ */ public interface QuotaAwareStore { /** * Get the quotas for the specified root element. * * @param root The root name for the quota information. * * @return An array of Quota objects defined for the root. * @throws MessagingException if the quotas cannot be retrieved */ public Quota[] getQuota(String root) throws javax.mail.MessagingException; /** * Set a quota item. The root contained in the Quota item identifies * the quota target. * * @param quota The source quota item. * @throws MessagingException if the quota cannot be set */ public void setQuota(Quota quota) throws javax.mail.MessagingException; } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/NoSuchProviderException.java0000664000175000017500000000226411345370365027461 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class NoSuchProviderException extends MessagingException { private static final long serialVersionUID = 8058319293154708827L; public NoSuchProviderException() { super(); } public NoSuchProviderException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Session.java0000664000175000017500000007471511350204263024312 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.WeakHashMap; import org.apache.geronimo.osgi.locator.ProviderLocator; import org.apache.geronimo.mail.MailProviderRegistry; /** * OK, so we have a final class in the API with a heck of a lot of implementation required... * let's try and figure out what it is meant to do. *

* It is supposed to collect together properties and defaults so that they can be * shared by multiple applications on a desktop; with process isolation and no * real concept of shared memory, this seems challenging. These properties and * defaults rely on system properties, making management in a app server harder, * and on resources loaded from "mail.jar" which may lead to skew between * differnet independent implementations of this API. * * @version $Rev: 924365 $ $Date: 2010-03-17 12:52:03 -0400 (Wed, 17 Mar 2010) $ */ public final class Session { private static final Class[] PARAM_TYPES = {Session.class, URLName.class}; private static final WeakHashMap addressMapsByClassLoader = new WeakHashMap(); private static Session DEFAULT_SESSION; private Map passwordAuthentications = new HashMap(); private final Properties properties; private final Authenticator authenticator; private boolean debug; private PrintStream debugOut = System.out; private static final WeakHashMap providersByClassLoader = new WeakHashMap(); /** * No public constrcutor allowed. */ private Session(Properties properties, Authenticator authenticator) { this.properties = properties; this.authenticator = authenticator; debug = Boolean.valueOf(properties.getProperty("mail.debug")).booleanValue(); } /** * Create a new session initialized with the supplied properties which uses the supplied authenticator. * Clients should ensure the properties listed in Appendix A of the JavaMail specification are * set as the defaults are unlikey to work in most scenarios; particular attention should be given * to: *

* * @param properties the session properties * @param authenticator an authenticator for callbacks to the user * @return a new session */ public static Session getInstance(Properties properties, Authenticator authenticator) { // if we have a properties bundle, we need a copy of the provided one if (properties != null) { properties = (Properties)properties.clone(); } return new Session(properties, authenticator); } /** * Create a new session initialized with the supplied properties with no authenticator. * * @param properties the session properties * @return a new session * @see #getInstance(java.util.Properties, Authenticator) */ public static Session getInstance(Properties properties) { return getInstance(properties, null); } /** * Get the "default" instance assuming no authenticator is required. * * @param properties the session properties * @return if "default" session * @throws SecurityException if the does not have permission to access the default session */ public synchronized static Session getDefaultInstance(Properties properties) { return getDefaultInstance(properties, null); } /** * Get the "default" session. * If there is not current "default", a new Session is created and installed as the default. * * @param properties * @param authenticator * @return if "default" session * @throws SecurityException if the does not have permission to access the default session */ public synchronized static Session getDefaultInstance(Properties properties, Authenticator authenticator) { if (DEFAULT_SESSION == null) { DEFAULT_SESSION = getInstance(properties, authenticator); } else { if (authenticator != DEFAULT_SESSION.authenticator) { if (authenticator == null || DEFAULT_SESSION.authenticator == null || authenticator.getClass().getClassLoader() != DEFAULT_SESSION.authenticator.getClass().getClassLoader()) { throw new SecurityException(); } } // todo we should check with the SecurityManager here as well } return DEFAULT_SESSION; } /** * Enable debugging for this session. * Debugging can also be enabled by setting the "mail.debug" property to true when * the session is being created. * * @param debug the debug setting */ public void setDebug(boolean debug) { this.debug = debug; } /** * Get the debug setting for this session. * * @return the debug setting */ public boolean getDebug() { return debug; } /** * Set the output stream where debug information should be sent. * If set to null, System.out will be used. * * @param out the stream to write debug information to */ public void setDebugOut(PrintStream out) { debugOut = out == null ? System.out : out; } /** * Return the debug output stream. * * @return the debug output stream */ public PrintStream getDebugOut() { return debugOut; } /** * Return the list of providers available to this application. * This method searches for providers that are defined in the javamail.providers * and javamail.default.providers resources available through the current context * classloader, or if that is not available, the classloader that loaded this class. *

* As searching for providers is potentially expensive, this implementation maintains * a WeakHashMap of providers indexed by ClassLoader. * * @return an array of providers */ public Provider[] getProviders() { ProviderInfo info = getProviderInfo(); return (Provider[]) info.all.toArray(new Provider[info.all.size()]); } /** * Return the provider for a specific protocol. * This implementation initially looks in the Session properties for an property with the name * "mail..class"; if found it attempts to create an instance of the class named in that * property throwing a NoSuchProviderException if the class cannot be loaded. * If this property is not found, it searches the providers returned by {@link #getProviders()} * for a entry for the specified protocol. * * @param protocol the protocol to get a provider for * @return a provider for that protocol * @throws NoSuchProviderException */ public Provider getProvider(String protocol) throws NoSuchProviderException { ProviderInfo info = getProviderInfo(); Provider provider = null; String providerName = properties.getProperty("mail." + protocol + ".class"); if (providerName != null) { provider = (Provider) info.byClassName.get(providerName); if (debug) { writeDebug("DEBUG: new provider loaded: " + provider.toString()); } } // if not able to locate this by class name, just grab a registered protocol. if (provider == null) { provider = (Provider) info.byProtocol.get(protocol); } if (provider == null) { throw new NoSuchProviderException("Unable to locate provider for protocol: " + protocol); } if (debug) { writeDebug("DEBUG: getProvider() returning provider " + provider.toString()); } return provider; } /** * Make the supplied Provider the default for its protocol. * * @param provider the new default Provider * @throws NoSuchProviderException */ public void setProvider(Provider provider) throws NoSuchProviderException { ProviderInfo info = getProviderInfo(); info.byProtocol.put(provider.getProtocol(), provider); } /** * Return a Store for the default protocol defined by the mail.store.protocol property. * * @return the store for the default protocol * @throws NoSuchProviderException */ public Store getStore() throws NoSuchProviderException { String protocol = properties.getProperty("mail.store.protocol"); if (protocol == null) { throw new NoSuchProviderException("mail.store.protocol property is not set"); } return getStore(protocol); } /** * Return a Store for the specified protocol. * * @param protocol the protocol to get a Store for * @return a Store * @throws NoSuchProviderException if no provider is defined for the specified protocol */ public Store getStore(String protocol) throws NoSuchProviderException { Provider provider = getProvider(protocol); return getStore(provider); } /** * Return a Store for the protocol specified in the given URL * * @param url the URL of the Store * @return a Store * @throws NoSuchProviderException if no provider is defined for the specified protocol */ public Store getStore(URLName url) throws NoSuchProviderException { return (Store) getService(getProvider(url.getProtocol()), url); } /** * Return the Store specified by the given provider. * * @param provider the provider to create from * @return a Store * @throws NoSuchProviderException if there was a problem creating the Store */ public Store getStore(Provider provider) throws NoSuchProviderException { if (Provider.Type.STORE != provider.getType()) { throw new NoSuchProviderException("Not a Store Provider: " + provider); } return (Store) getService(provider, null); } /** * Return a closed folder for the supplied URLName, or null if it cannot be obtained. *

* The scheme portion of the URL is used to locate the Provider and create the Store; * the returned Store is then used to obtain the folder. * * @param name the location of the folder * @return the requested folder, or null if it is unavailable * @throws NoSuchProviderException if there is no provider * @throws MessagingException if there was a problem accessing the Store */ public Folder getFolder(URLName name) throws MessagingException { Store store = getStore(name); return store.getFolder(name); } /** * Return a Transport for the default protocol specified by the * mail.transport.protocol property. * * @return a Transport * @throws NoSuchProviderException */ public Transport getTransport() throws NoSuchProviderException { String protocol = properties.getProperty("mail.transport.protocol"); if (protocol == null) { throw new NoSuchProviderException("mail.transport.protocol property is not set"); } return getTransport(protocol); } /** * Return a Transport for the specified protocol. * * @param protocol the protocol to use * @return a Transport * @throws NoSuchProviderException */ public Transport getTransport(String protocol) throws NoSuchProviderException { Provider provider = getProvider(protocol); return getTransport(provider); } /** * Return a transport for the protocol specified in the URL. * * @param name the URL whose scheme specifies the protocol * @return a Transport * @throws NoSuchProviderException */ public Transport getTransport(URLName name) throws NoSuchProviderException { return (Transport) getService(getProvider(name.getProtocol()), name); } /** * Return a transport for the protocol associated with the type of this address. * * @param address the address we are trying to deliver to * @return a Transport * @throws NoSuchProviderException */ public Transport getTransport(Address address) throws NoSuchProviderException { String type = address.getType(); // load the address map from the resource files. Map addressMap = getAddressMap(); String protocolName = (String)addressMap.get(type); if (protocolName == null) { throw new NoSuchProviderException("No provider for address type " + type); } return getTransport(protocolName); } /** * Return the Transport specified by a Provider * * @param provider the defining Provider * @return a Transport * @throws NoSuchProviderException */ public Transport getTransport(Provider provider) throws NoSuchProviderException { return (Transport) getService(provider, null); } /** * Set the password authentication associated with a URL. * * @param name the url * @param authenticator the authenticator */ public void setPasswordAuthentication(URLName name, PasswordAuthentication authenticator) { if (authenticator == null) { passwordAuthentications.remove(name); } else { passwordAuthentications.put(name, authenticator); } } /** * Get the password authentication associated with a URL * * @param name the URL * @return any authenticator for that url, or null if none */ public PasswordAuthentication getPasswordAuthentication(URLName name) { return (PasswordAuthentication) passwordAuthentications.get(name); } /** * Call back to the application supplied authenticator to get the needed username add password. * * @param host the host we are trying to connect to, may be null * @param port the port on that host * @param protocol the protocol trying to be used * @param prompt a String to show as part of the prompt, may be null * @param defaultUserName the default username, may be null * @return the authentication information collected by the authenticator; may be null */ public PasswordAuthentication requestPasswordAuthentication(InetAddress host, int port, String protocol, String prompt, String defaultUserName) { if (authenticator == null) { return null; } return authenticator.authenticate(host, port, protocol, prompt, defaultUserName); } /** * Return the properties object for this Session; this is a live collection. * * @return the properties for the Session */ public Properties getProperties() { return properties; } /** * Return the specified property. * * @param property the property to get * @return its value, or null if not present */ public String getProperty(String property) { return getProperties().getProperty(property); } /** * Add a provider to the Session managed provider list. * * @param provider The new provider to add. */ public synchronized void addProvider(Provider provider) { ProviderInfo info = getProviderInfo(); info.addProvider(provider); } /** * Add a mapping between an address type and a protocol used * to process that address type. * * @param addressType * The address type identifier. * @param protocol The protocol name mapping. */ public void setProtocolForAddress(String addressType, String protocol) { Map addressMap = getAddressMap(); // no protocol specified is a removal if (protocol == null) { addressMap.remove(addressType); } else { addressMap.put(addressType, protocol); } } private Service getService(Provider provider, URLName name) throws NoSuchProviderException { try { if (name == null) { name = new URLName(provider.getProtocol(), null, -1, null, null, null); } ClassLoader cl = getClassLoader(); Class clazz = null; try { clazz = ProviderLocator.loadClass(provider.getClassName(), this.getClass(), cl); } catch (ClassNotFoundException e) { throw (NoSuchProviderException) new NoSuchProviderException("Unable to load class for provider: " + provider).initCause(e); } Constructor ctr = clazz.getConstructor(PARAM_TYPES); return(Service) ctr.newInstance(new Object[]{this, name}); } catch (NoSuchMethodException e) { throw (NoSuchProviderException) new NoSuchProviderException("Provider class does not have a constructor(Session, URLName): " + provider).initCause(e); } catch (InstantiationException e) { throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e); } catch (IllegalAccessException e) { throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e); } catch (InvocationTargetException e) { throw (NoSuchProviderException) new NoSuchProviderException("Exception from constructor of provider class: " + provider).initCause(e.getCause()); } } private ProviderInfo getProviderInfo() { ClassLoader cl = getClassLoader(); synchronized (providersByClassLoader) { ProviderInfo info = (ProviderInfo) providersByClassLoader.get(cl); if (info == null) { info = loadProviders(cl); } return info; } } private Map getAddressMap() { ClassLoader cl = getClassLoader(); Map addressMap = (Map)addressMapsByClassLoader.get(cl); if (addressMap == null) { addressMap = loadAddressMap(cl); } return addressMap; } /** * Resolve a class loader used to resolve context resources. The * class loader used is either a current thread context class * loader (if set), the class loader used to load an authenticator * we've been initialized with, or the class loader used to load * this class instance (which may be a subclass of Session). * * @return The class loader used to load resources. */ private ClassLoader getClassLoader() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { if (authenticator != null) { cl = authenticator.getClass().getClassLoader(); } else { cl = this.getClass().getClassLoader(); } } return cl; } private ProviderInfo loadProviders(ClassLoader cl) { // we create a merged map from reading all of the potential address map entries. The locations // searched are: // 1. java.home/lib/javamail.address.map // 2. META-INF/javamail.address.map // 3. META-INF/javamail.default.address.map // ProviderInfo info = new ProviderInfo(); // NOTE: Unlike the addressMap, we process these in the defined order. The loading routine // will not overwrite entries if they already exist in the map. try { File file = new File(System.getProperty("java.home"), "lib/javamail.providers"); InputStream is = new FileInputStream(file); try { loadProviders(info, is); if (debug) { writeDebug("Loaded lib/javamail.providers from " + file.toString()); } } finally{ is.close(); } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } try { Enumeration e = cl.getResources("META-INF/javamail.providers"); while (e.hasMoreElements()) { URL url = (URL) e.nextElement(); if (debug) { writeDebug("Loading META-INF/javamail.providers from " + url.toString()); } InputStream is = url.openStream(); try { loadProviders(info, is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } // we could be running in an OSGi environment, so there might be some globally defined // providers try { Collection l = MailProviderRegistry.getProviders(); for (URL url : l) { if (debug) { writeDebug("Loading META-INF/javamail.providers from " + url.toString()); } InputStream is = url.openStream(); try { loadProviders(info, is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } try { Enumeration e = cl.getResources("META-INF/javamail.default.providers"); while (e.hasMoreElements()) { URL url = (URL) e.nextElement(); if (debug) { writeDebug("Loading javamail.default.providers from " + url.toString()); } InputStream is = url.openStream(); try { loadProviders(info, is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } // we could be running in an OSGi environment, so there might be some globally defined // providers try { Collection l = MailProviderRegistry.getDefaultProviders(); for (URL url : l) { if (debug) { writeDebug("Loading META-INF/javamail.providers from " + url.toString()); } InputStream is = url.openStream(); try { loadProviders(info, is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } // make sure this is added to the global map. providersByClassLoader.put(cl, info); return info; } private void loadProviders(ProviderInfo info, InputStream is) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null) { // Lines beginning with "#" are just comments. if (line.startsWith("#")) { continue; } StringTokenizer tok = new StringTokenizer(line, ";"); String protocol = null; Provider.Type type = null; String className = null; String vendor = null; String version = null; while (tok.hasMoreTokens()) { String property = tok.nextToken(); int index = property.indexOf('='); if (index == -1) { continue; } String key = property.substring(0, index).trim().toLowerCase(); String value = property.substring(index+1).trim(); if (protocol == null && "protocol".equals(key)) { protocol = value; } else if (type == null && "type".equals(key)) { if ("store".equals(value)) { type = Provider.Type.STORE; } else if ("transport".equals(value)) { type = Provider.Type.TRANSPORT; } } else if (className == null && "class".equals(key)) { className = value; } else if ("vendor".equals(key)) { vendor = value; } else if ("version".equals(key)) { version = value; } } if (protocol == null || type == null || className == null) { //todo should we log a warning? continue; } if (debug) { writeDebug("DEBUG: loading new provider protocol=" + protocol + ", className=" + className + ", vendor=" + vendor + ", version=" + version); } Provider provider = new Provider(type, protocol, className, vendor, version); // add to the info list. info.addProvider(provider); } } /** * Load up an address map associated with a using class loader * instance. * * @param cl The class loader used to resolve the address map. * * @return A map containing the entries associated with this classloader * instance. */ private static Map loadAddressMap(ClassLoader cl) { // we create a merged map from reading all of the potential address map entries. The locations // searched are: // 1. java.home/lib/javamail.address.map // 2. META-INF/javamail.address.map // 3. META-INF/javamail.default.address.map // // if all of the above searches fail, we just set up some "default" defaults. // the format of the address.map file is defined as a property file. We can cheat and // just use Properties.load() to read in the files. Properties addressMap = new Properties(); // add this to the tracking map. addressMapsByClassLoader.put(cl, addressMap); // NOTE: We are reading these resources in reverse order of what's cited above. This allows // user defined entries to overwrite default entries if there are similarly named items. try { Enumeration e = cl.getResources("META-INF/javamail.default.address.map"); while (e.hasMoreElements()) { URL url = (URL) e.nextElement(); InputStream is = url.openStream(); try { // load as a property file addressMap.load(is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } try { Enumeration e = cl.getResources("META-INF/javamail.address.map"); while (e.hasMoreElements()) { URL url = (URL) e.nextElement(); InputStream is = url.openStream(); try { // load as a property file addressMap.load(is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } try { File file = new File(System.getProperty("java.home"), "lib/javamail.address.map"); InputStream is = new FileInputStream(file); try { // load as a property file addressMap.load(is); } finally{ is.close(); } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } try { Enumeration e = cl.getResources("META-INF/javamail.address.map"); while (e.hasMoreElements()) { URL url = (URL) e.nextElement(); InputStream is = url.openStream(); try { // load as a property file addressMap.load(is); } finally{ is.close(); } } } catch (SecurityException e) { // ignore } catch (IOException e) { // ignore } // if unable to load anything, at least create the MimeMessage-smtp protocol mapping. if (addressMap.isEmpty()) { addressMap.put("rfc822", "smtp"); } return addressMap; } /** * Private convenience routine for debug output. * * @param msg The message to write out to the debug stream. */ private void writeDebug(String msg) { debugOut.println(msg); } private static class ProviderInfo { private final Map byClassName = new HashMap(); private final Map byProtocol = new HashMap(); private final List all = new ArrayList(); public void addProvider(Provider provider) { String className = provider.getClassName(); if (!byClassName.containsKey(className)) { byClassName.put(className, provider); } String protocol = provider.getProtocol(); if (!byProtocol.containsKey(protocol)) { byProtocol.put(protocol, provider); } all.add(provider); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/ReadOnlyFolderException.java0000664000175000017500000000257611345370365027426 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class ReadOnlyFolderException extends MessagingException { private static final long serialVersionUID = 5711829372799039325L; private transient Folder _folder; public ReadOnlyFolderException(Folder folder) { this(folder, "Folder not found: " + folder.getName()); } public ReadOnlyFolderException(Folder folder, String message) { super(message); _folder = folder; } public Folder getFolder() { return _folder; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MethodNotSupportedException.java0000664000175000017500000000230111345370365030346 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MethodNotSupportedException extends MessagingException { private static final long serialVersionUID = -3757386618726131322L; public MethodNotSupportedException() { super(); } public MethodNotSupportedException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Address.java0000664000175000017500000000347611345370365024263 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.Serializable; /** * This abstract class models the addresses in a message. * Addresses are Serializable so that they may be serialized along with other search terms. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class Address implements Serializable { private static final long serialVersionUID = -5822459626751992278L; /** * Subclasses must provide a suitable implementation of equals(). * * @param object the object to compare t * @return true if the subclass determines the other object is equal to this Address */ public abstract boolean equals(Object object); /** * Return a String that identifies this address type. * @return the type of this address */ public abstract String getType(); /** * Subclasses must provide a suitable representation of their address. * @return a representation of an Address as a String */ public abstract String toString(); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/util/0000775000175000017500000000000011703375731022776 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/util/SharedByteArrayInputStream.java0000664000175000017500000000553410517560657031102 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.mail.internet.SharedInputStream; public class SharedByteArrayInputStream extends ByteArrayInputStream implements SharedInputStream { /** * Position within shared buffer that this stream starts at. */ protected int start; /** * Create a SharedByteArrayInputStream that shares the entire * buffer. * * @param buf The input data. */ public SharedByteArrayInputStream(byte[] buf) { this(buf, 0, buf.length); } /** * Create a SharedByteArrayInputStream using a subset of the * array data. * * @param buf The source data array. * @param offset The starting offset within the array. * @param length The length of data to use. */ public SharedByteArrayInputStream(byte[] buf, int offset, int length) { super(buf, offset, length); start = offset; } /** * Get the position within the output stream, adjusted by the * starting offset. * * @return The adjusted position within the stream. */ public long getPosition() { return pos - start; } /** * Create a new input stream from this input stream, accessing * a subset of the data. Think of it as a substring operation * for a stream. * * The starting offset must be non-negative. The end offset can * by -1, which means use the remainder of the stream. * * @param offset The starting offset. * @param end The end offset (which can be -1). * * @return An InputStream configured to access the indicated data subrange. */ public InputStream newStream(long offset, long end) { if (offset < 0) { throw new IllegalArgumentException("Starting position must be non-negative"); } if (end == -1) { end = count - start; } return new SharedByteArrayInputStream(buf, start + (int)offset, (int)(end - offset)); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/util/SharedFileInputStream.java0000664000175000017500000004551411026462625030051 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import javax.mail.internet.SharedInputStream; public class SharedFileInputStream extends BufferedInputStream implements SharedInputStream { // This initial size isn't documented, but bufsize is 2048 after initialization for the // Sun implementation. private static final int DEFAULT_BUFFER_SIZE = 2048; // the shared file information, used to synchronize opens/closes of the base file. private SharedFileSource source; /** * The file offset that is the first byte in the read buffer. */ protected long bufpos; /** * The normal size of the read buffer. */ protected int bufsize; /** * The size of the file subset represented by this stream instance. */ protected long datalen; /** * The source of the file data. This is shared across multiple * instances. */ protected RandomAccessFile in; /** * The starting position of data represented by this stream relative * to the start of the file data. This stream instance represents * data in the range start to (start + datalen - 1). */ protected long start; /** * Construct a SharedFileInputStream from a file name, using the default buffer size. * * @param file The name of the file. * * @exception IOException */ public SharedFileInputStream(String file) throws IOException { this(file, DEFAULT_BUFFER_SIZE); } /** * Construct a SharedFileInputStream from a File object, using the default buffer size. * * @param file The name of the file. * * @exception IOException */ public SharedFileInputStream(File file) throws IOException { this(file, DEFAULT_BUFFER_SIZE); } /** * Construct a SharedFileInputStream from a file name, with a given initial buffer size. * * @param file The name of the file. * @param bufferSize The initial buffer size. * * @exception IOException */ public SharedFileInputStream(String file, int bufferSize) throws IOException { // I'm not sure this is correct or not. The SharedFileInputStream spec requires this // be a subclass of BufferedInputStream. The BufferedInputStream constructor takes a stream, // which we're not really working from at this point. Using null seems to work so far. super(null); init(new File(file), bufferSize); } /** * Construct a SharedFileInputStream from a File object, with a given initial buffer size. * * @param file The name of the file. * @param bufferSize The initial buffer size. * * @exception IOException */ public SharedFileInputStream(File file, int bufferSize) throws IOException { // I'm not sure this is correct or not. The SharedFileInputStream spec requires this // be a subclass of BufferedInputStream. The BufferedInputStream constructor takes a stream, // which we're not really working from at this point. Using null seems to work so far. super(null); init(file, bufferSize); } /** * Private constructor used to spawn off a shared instance * of this stream. * * @param source The internal class object that manages the shared resources of * the stream. * @param start The starting offset relative to the beginning of the file. * @param len The length of file data in this shared instance. * @param bufsize The initial buffer size (same as the spawning parent. */ private SharedFileInputStream(SharedFileSource source, long start, long len, int bufsize) { super(null); this.source = source; in = source.open(); this.start = start; bufpos = start; datalen = len; this.bufsize = bufsize; buf = new byte[bufsize]; // other fields such as pos and count initialized by the super class constructor. } /** * Shared initializtion routine for the constructors. * * @param file The file we're accessing. * @param bufferSize The initial buffer size to use. * * @exception IOException */ private void init(File file, int bufferSize) throws IOException { if (bufferSize <= 0) { throw new IllegalArgumentException("Buffer size must be positive"); } // create a random access file for accessing the data, then create an object that's used to share // instances of the same stream. source = new SharedFileSource(file); // we're opening the first one. in = source.open(); // this represents the entire file, for now. start = 0; // use the current file length for the bounds datalen = in.length(); // now create our buffer version bufsize = bufferSize; bufpos = 0; // NB: this is using the super class protected variable. buf = new byte[bufferSize]; } /** * Check to see if we need to read more data into our buffer. * * @return False if there's not valid data in the buffer (generally means * an EOF condition). * @exception IOException */ private boolean checkFill() throws IOException { // if we have data in the buffer currently, just return if (pos < count) { return true; } // ugh, extending BufferedInputStream also means supporting mark positions. That complicates everything. // life is so much easier if marks are not used.... if (markpos < 0) { // reset back to the buffer position pos = 0; // this will be the new position within the file once we're read some data. bufpos += count; } else { // we have marks to worry about....damn. // if we have room in the buffer to read more data, then we will. Otherwise, we need to see // if it's possible to shift the data in the buffer or extend the buffer (up to the mark limit). if (pos >= buf.length) { // the mark position is not at the beginning of the buffer, so just shuffle the bytes, leaving // us room to read more data. if (markpos > 0) { // this is the size of the data we need to keep. int validSize = pos - markpos; // perform the shift operation. System.arraycopy(buf, markpos, buf, 0, validSize); // now adjust the positional markers for this shift. pos = validSize; bufpos += markpos; markpos = 0; } // the mark is at the beginning, and we've used up the buffer. See if we're allowed to // extend this. else if (buf.length < marklimit) { // try to double this, but throttle to the mark limit int newSize = Math.min(buf.length * 2, marklimit); byte[] newBuffer = new byte[newSize]; System.arraycopy(buf, 0, newBuffer, 0, buf.length); // replace the old buffer. Note that all other positional markers remain the same here. buf = newBuffer; } // we've got further than allowed, so invalidate the mark, and just reset the buffer else { markpos = -1; pos = 0; bufpos += count; } } } // if we're past our designated end, force an eof. if (bufpos + pos >= start + datalen) { // make sure we zero the count out, otherwise we'll reuse this data // if called again. count = pos; return false; } // seek to the read location start. Note this is a shared file, so this assumes all of the methods // doing buffer fills will be synchronized. int fillLength = buf.length - pos; // we might be working with a subset of the file data, so normal eof processing might not apply. // we need to limit how much we read to the data length. if (bufpos - start + pos + fillLength > datalen) { fillLength = (int)(datalen - (bufpos - start + pos)); } // finally, try to read more data into the buffer. fillLength = source.read(bufpos + pos, buf, pos, fillLength); // we weren't able to read anything, count this as an eof failure. if (fillLength <= 0) { // make sure we zero the count out, otherwise we'll reuse this data // if called again. count = pos; return false; } // set the new buffer count count = fillLength + pos; // we have data in the buffer. return true; } /** * Return the number of bytes available for reading without * blocking for a long period. * * @return For this stream, this is the number of bytes between the * current read position and the indicated end of the file. * @exception IOException */ public synchronized int available() throws IOException { checkOpen(); // this is backed by a file, which doesn't really block. We can return all the way to the // marked data end, if necessary long endMarker = start + datalen; return (int)(endMarker - (bufpos + pos)); } /** * Return the current read position of the stream. * * @return The current position relative to the beginning of the stream. * This is not the position relative to the start of the file, since * the stream starting position may be other than the beginning. */ public long getPosition() { checkOpenRuntime(); return bufpos + pos - start; } /** * Mark the current position for retracing. * * @param readlimit The limit for the distance the read position can move from * the mark position before the mark is reset. */ public synchronized void mark(int readlimit) { checkOpenRuntime(); marklimit = readlimit; markpos = pos; } /** * Read a single byte of data from the input stream. * * @return The read byte. Returns -1 if an eof condition has been hit. * @exception IOException */ public synchronized int read() throws IOException { checkOpen(); // check to see if we can fill more data if (!checkFill()) { return -1; } // return the current byte...anded to prevent sign extension. return buf[pos++] & 0xff; } /** * Read multiple bytes of data and place them directly into * a byte-array buffer. * * @param buffer The target buffer. * @param offset The offset within the buffer to place the data. * @param length The length to attempt to read. * * @return The number of bytes actually read. Returns -1 for an EOF * condition. * @exception IOException */ public synchronized int read(byte buffer[], int offset, int length) throws IOException { checkOpen(); // asked to read nothing? That's what we'll do. if (length == 0) { return 0; } int returnCount = 0; while (length > 0) { // check to see if we can/must fill more data if (!checkFill()) { // we've hit the end, but if we've read data, then return that. if (returnCount > 0) { return returnCount; } // trun eof. return -1; } int available = count - pos; int given = Math.min(available, length); System.arraycopy(buf, pos, buffer, offset, given); // now adjust all of our positions and counters pos += given; length -= given; returnCount += given; offset += given; } // return the accumulated count. return returnCount; } /** * Skip the read pointer ahead a given number of bytes. * * @param n The number of bytes to skip. * * @return The number of bytes actually skipped. * @exception IOException */ public synchronized long skip(long n) throws IOException { checkOpen(); // nothing to skip, so don't skip if (n <= 0) { return 0; } // see if we need to fill more data, and potentially shift the mark positions if (!checkFill()) { return 0; } long available = count - pos; // the skipped contract allows skipping within the current buffer bounds, so cap it there. long skipped = available < n ? available : n; pos += skipped; return skipped; } /** * Reset the mark position. * * @exception IOException */ public synchronized void reset() throws IOException { checkOpen(); if (markpos < 0) { throw new IOException("Resetting to invalid mark position"); } // if we have a markpos, it will still be in the buffer bounds. pos = markpos; } /** * Indicates the mark() operation is supported. * * @return Always returns true. */ public boolean markSupported() { return true; } /** * Close the stream. This does not close the source file until * the last shared instance is closed. * * @exception IOException */ public void close() throws IOException { // already closed? This is not an error if (in == null) { return; } try { // perform a close on the source version. source.close(); } finally { in = null; } } /** * Create a new stream from this stream, using the given * start offset and length. * * @param offset The offset relative to the start of this stream instance. * @param end The end offset of the substream. If -1, the end of the parent stream is used. * * @return A new SharedFileInputStream object sharing the same source * input file. */ public InputStream newStream(long offset, long end) { checkOpenRuntime(); if (offset < 0) { throw new IllegalArgumentException("Start position is less than 0"); } // the default end position is the datalen of the one we're spawning from. if (end == -1) { end = datalen; } // create a new one using the private constructor return new SharedFileInputStream(source, start + (int)offset, (int)(end - offset), bufsize); } /** * Check if the file is open and throw an IOException if not. * * @exception IOException */ private void checkOpen() throws IOException { if (in == null) { throw new IOException("Stream has been closed"); } } /** * Check if the file is open and throw an IOException if not. This version is * used because several API methods are not defined as throwing IOException, so * checkOpen() can't be used. The Sun implementation just throws RuntimeExceptions * in those methods, hence 2 versions. * * @exception RuntimeException */ private void checkOpenRuntime() { if (in == null) { throw new RuntimeException("Stream has been closed"); } } /** * Internal class used to manage resources shared between the * ShareFileInputStream instances. */ class SharedFileSource { // the file source public RandomAccessFile source; // the shared instance count for this file (open instances) public int instanceCount = 0; public SharedFileSource(File file) throws IOException { source = new RandomAccessFile(file, "r"); } /** * Open the shared stream to keep track of open instances. */ public synchronized RandomAccessFile open() { instanceCount++; return source; } /** * Process a close request for this stream. If there are multiple * instances using this underlying stream, the stream will not * be closed. * * @exception IOException */ public synchronized void close() throws IOException { if (instanceCount > 0) { instanceCount--; // if the last open instance, close the real source file. if (instanceCount == 0) { source.close(); } } } /** * Read a buffer of data from the shared file. * * @param position The position to read from. * @param buf The target buffer for storing the read data. * @param offset The starting offset within the buffer. * @param length The length to attempt to read. * * @return The number of bytes actually read. * @exception IOException */ public synchronized int read(long position, byte[] buf, int offset, int length) throws IOException { // seek to the read location start. Note this is a shared file, so this assumes all of the methods // doing buffer fills will be synchronized. source.seek(position); return source.read(buf, offset, length); } /** * Ensure the stream is closed when this shared object is finalized. * * @exception Throwable */ protected void finalize() throws Throwable { super.finalize(); if (instanceCount > 0) { source.close(); } } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/util/ByteArrayDataSource.java0000664000175000017500000001205010517560657027521 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.activation.DataSource; import javax.mail.internet.ContentType; import javax.mail.internet.ParseException; import javax.mail.internet.MimeUtility; /** * An activation DataSource object that sources the data from * a byte[] array. * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class ByteArrayDataSource implements DataSource { // the data source private byte[] source; // the content MIME type private String contentType; // the name information (defaults to a null string) private String name = ""; /** * Create a ByteArrayDataSource from an input stream. * * @param in The source input stream. * @param type The MIME-type of the data. * * @exception IOException */ public ByteArrayDataSource(InputStream in, String type) throws IOException { ByteArrayOutputStream sink = new ByteArrayOutputStream(); // ok, how I wish you could just pipe an input stream into an output stream :-) byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = in.read(buffer)) > 0) { sink.write(buffer, 0, bytesRead); } source = sink.toByteArray(); contentType = type; } /** * Create a ByteArrayDataSource directly from a byte array. * * @param data The source byte array (not copied). * @param type The content MIME-type. */ public ByteArrayDataSource(byte[] data, String type) { source = data; contentType = type; } /** * Create a ByteArrayDataSource from a string value. If the * type information includes a charset parameter, that charset * is used to extract the bytes. Otherwise, the default Java * char set is used. * * @param data The source data string. * @param type The MIME type information. * * @exception IOException */ public ByteArrayDataSource(String data, String type) throws IOException { String charset = null; try { // the charset can be encoded in the content type, which we parse using // the ContentType class. ContentType content = new ContentType(type); charset = content.getParameter("charset"); } catch (ParseException e) { // ignored...just use the default if this fails } if (charset == null) { charset = MimeUtility.getDefaultJavaCharset(); } else { // the type information encodes a MIME charset, which may need mapping to a Java one. charset = MimeUtility.javaCharset(charset); } // get the source using the specified charset source = data.getBytes(charset); contentType = type; } /** * Create an input stream for this data. A new input stream * is created each time. * * @return An InputStream for reading the encapsulated data. * @exception IOException */ public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(source); } /** * Open an output stream for the DataSource. This is not * supported by this DataSource, so an IOException is always * throws. * * @return Nothing...an IOException is always thrown. * @exception IOException */ public OutputStream getOutputStream() throws IOException { throw new IOException("Writing to a ByteArrayDataSource is not supported"); } /** * Get the MIME content type information for this DataSource. * * @return The MIME content type string. */ public String getContentType() { return contentType; } /** * Retrieve the DataSource name. If not explicitly set, this * returns "". * * @return The currently set DataSource name. */ public String getName() { return name; } /** * Set a new DataSource name. * * @param name The new name. */ public void setName(String name) { this.name = name; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Authenticator.java0000664000175000017500000000364510517560657025513 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.net.InetAddress; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class Authenticator { private InetAddress host; private int port; private String prompt; private String protocol; private String username; synchronized PasswordAuthentication authenticate(InetAddress host, int port, String protocol, String prompt, String username) { this.host = host; this.port = port; this.protocol = protocol; this.prompt = prompt; this.username = username; return getPasswordAuthentication(); } protected final String getDefaultUserName() { return username; } protected PasswordAuthentication getPasswordAuthentication() { return null; } protected final int getRequestingPort() { return port; } protected final String getRequestingPrompt() { return prompt; } protected final String getRequestingProtocol() { return protocol; } protected final InetAddress getRequestingSite() { return host; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/FolderClosedException.java0000664000175000017500000000256611345370365027121 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class FolderClosedException extends MessagingException { private static final long serialVersionUID = 1687879213433302315L; private transient Folder _folder; public FolderClosedException(Folder folder) { this(folder, "Folder Closed: " + folder.getName()); } public FolderClosedException(Folder folder, String message) { super(message); _folder = folder; } public Folder getFolder() { return _folder; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MessageContext.java0000664000175000017500000000515710675734274025635 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * The context in which a piece of message content is contained. * * @version $Rev: 578802 $ $Date: 2007-09-24 09:16:44 -0400 (Mon, 24 Sep 2007) $ */ public class MessageContext { private final Part part; /** * Create a MessageContext object describing the context of the supplied Part. * * @param part the containing part */ public MessageContext(Part part) { this.part = part; } /** * Return the {@link Part} that contains the content. * * @return the part */ public Part getPart() { return part; } /** * Return the message that contains the content; if the Part is a {@link Multipart} * then recurse up the chain until a {@link Message} is found. * * @return */ public Message getMessage() { return getMessageFrom(part); } /** * Return the session associated with the Message containing this Part. * * @return the session associated with this context's root message */ public Session getSession() { Message message = getMessage(); if (message == null) { return null; } else { return message.session; } } /** * recurse up the chain of MultiPart/BodyPart parts until we hit a message * * @param p The starting part. * * @return The encountered Message or null if no Message parts * are found. */ private Message getMessageFrom(Part p) { while (p != null) { if (p instanceof Message) { return (Message) p; } Multipart mp = ((BodyPart) p).getParent(); if (mp == null) { return null; } p = mp.getParent(); } return null; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/FolderNotFoundException.java0000664000175000017500000000304611345370365027436 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class FolderNotFoundException extends MessagingException { private static final long serialVersionUID = 472612108891249403L; private transient Folder _folder; public FolderNotFoundException() { super(); } public FolderNotFoundException(Folder folder) { this(folder, "Folder not found: " + folder.getName()); } public FolderNotFoundException(Folder folder, String message) { super(message); _folder = folder; } public FolderNotFoundException(String message, Folder folder) { this(folder, message); } public Folder getFolder() { return _folder; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/EventQueue.java0000664000175000017500000001451010702444457024753 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ // // This source code implements specifications defined by the Java // Community Process. In order to remain compliant with the specification // DO NOT add / change / or delete method signatures! // package javax.mail; import java.util.LinkedList; import java.util.List; import javax.mail.event.MailEvent; /** * This is an event queue to dispatch javamail events on separate threads * from the main thread. EventQueues are created by javamail Services * (Transport and Store instances), as well as Folders created from Store * instances. Each entity will have its own private EventQueue instance, but * will delay creating it until it has an event to dispatch to a real listener. * * NOTE: It would be nice to use the concurrency support in Java 5 to * manage the queue, but this code needs to run on Java 1.4 still. We also * don't want to have dependencies on other packages with this, so no * outside concurrency packages can be used either. * @version $Rev: 582842 $ $Date: 2007-10-08 11:13:51 -0400 (Mon, 08 Oct 2007) $ */ class EventQueue implements Runnable { /** * The dispatch thread that handles notification events. */ protected Thread dispatchThread; /** * The dispatching queue for events. */ protected List eventQueue = new LinkedList(); /** * Create a new EventQueue, including starting the new thread. */ public EventQueue() { dispatchThread = new Thread(this, "JavaMail-EventQueue"); dispatchThread.setDaemon(true); // this is a background server thread. // start the thread up dispatchThread.start(); } /** * When an object implementing interface Runnable is used * to create a thread, starting the thread causes the object's * run method to be called in that separately executing * thread. *

* The general contract of the method run is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public void run() { try { while (true) { // get the next event PendingEvent p = dequeueEvent(); // an empty event on the queue means time to shut things down. if (p.event == null) { return; } // and tap the listeners on the shoulder. dispatchEvent(p.event, p.listeners); } } catch (InterruptedException e) { // been told to stop, so we stop } } /** * Stop the EventQueue. This will terminate the dispatcher thread as soon * as it can, so there may be undispatched events in the queue that will * not get dispatched. */ public synchronized void stop() { // if the thread has not been stopped yet, interrupt it // and clear the reference. if (dispatchThread != null) { // push a dummy marker on to the event queue // to force the dispatch thread to wake up. queueEvent(null, null); dispatchThread = null; } } /** * Add a new event to the queue. * * @param event The event to dispatch. * @param listeners The List of listeners to dispatch this to. This is assumed to be a * static snapshot of the listeners that will not change between the time * the event is queued and the dispatcher thread makes the calls to the * handlers. */ public synchronized void queueEvent(MailEvent event, List listeners) { // add an element to the list, then notify the processing thread. // Note that we make a copy of the listeners list. This ensures // we're going to dispatch this to the snapshot of the listeners PendingEvent p = new PendingEvent(event, listeners); eventQueue.add(p); // wake up the dispatch thread notify(); } /** * Remove the next event from the message queue. * * @return The PendingEvent item from the queue. */ protected synchronized PendingEvent dequeueEvent() throws InterruptedException { // a little spin loop to wait for an event while (eventQueue.isEmpty()) { wait(); } // just remove the first element of this return (PendingEvent)eventQueue.remove(0); } /** * Dispatch an event to a list of listeners. Any exceptions thrown by * the listeners will be swallowed. * * @param event The event to dispatch. * @param listeners The list of listeners this gets dispatched to. */ protected void dispatchEvent(MailEvent event, List listeners) { // iterate through the listeners list calling the handlers. for (int i = 0; i < listeners.size(); i++) { try { event.dispatch(listeners.get(i)); } catch (Throwable e) { // just eat these } } } /** * Small helper class to give a single reference handle for a pending event. */ class PendingEvent { // the event we're broadcasting MailEvent event; // the list of listeners we send this to. List listeners; PendingEvent(MailEvent event, List listeners) { this.event = event; this.listeners = listeners; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/0000775000175000017500000000000011703375730023265 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/BodyTerm.java0000664000175000017500000000466211345370365025666 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.io.IOException; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Part; import javax.mail.Multipart; import javax.mail.BodyPart; /** * Term that matches on a message body. All {@link javax.mail.BodyPart parts} that have * a MIME type of "text/*" are searched. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class BodyTerm extends StringTerm { private static final long serialVersionUID = -4888862527916911385L; public BodyTerm(String pattern) { super(pattern); } public boolean match(Message message) { try { return matchPart(message); } catch (IOException e) { return false; } catch (MessagingException e) { return false; } } private boolean matchPart(Part part) throws MessagingException, IOException { if (part.isMimeType("multipart/*")) { Multipart mp = (Multipart) part.getContent(); int count = mp.getCount(); for (int i=0; i < count; i++) { BodyPart bp = mp.getBodyPart(i); if (matchPart(bp)) { return true; } } return false; } else if (part.isMimeType("text/*")) { String content = (String) part.getContent(); return super.match(content); } else if (part.isMimeType("message/rfc822")) { // nested messages need recursion return matchPart((Part)part.getContent()); } else { return false; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/SentDateTerm.java0000664000175000017500000000332711345370365026475 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.util.Date; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class SentDateTerm extends DateTerm { private static final long serialVersionUID = 5647755030530907263L; public SentDateTerm(int comparison, Date date) { super(comparison, date); } public boolean match(Message message) { try { Date date = message.getSentDate(); if (date == null) { return false; } return match(message.getSentDate()); } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (this == other) return true; if (other instanceof SentDateTerm == false) return false; return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/SearchTerm.java0000664000175000017500000000313311345370365026166 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.io.Serializable; import javax.mail.Message; /** * Base class for Terms in a parse tree used to represent a search condition. * * This class is Serializable to allow for the short term persistence of * searches between Sessions; this is not intended for long-term persistence. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class SearchTerm implements Serializable { private static final long serialVersionUID = -6652358452205992789L; /** * Checks a matching criteria defined by the concrete subclass of this Term. * * @param message the message to apply the matching criteria to * @return true if the matching criteria is met */ public abstract boolean match(Message message); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/SubjectTerm.java0000664000175000017500000000323011345370365026356 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class SubjectTerm extends StringTerm { private static final long serialVersionUID = 7481568618055573432L; public SubjectTerm(String subject) { super(subject); } public boolean match(Message message) { try { String subject = message.getSubject(); if (subject == null) { return false; } return match(subject); } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (this == other) return true; if (other instanceof SubjectTerm == false) return false; return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/FromStringTerm.java0000664000175000017500000000322611345370365027056 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class FromStringTerm extends AddressStringTerm { private static final long serialVersionUID = 5801127523826772788L; public FromStringTerm(String string) { super(string); } public boolean match(Message message) { try { Address from[] = message.getFrom(); if (from == null) { return false; } for (int i = 0; i < from.length; i++) { if (match(from[i])){ return true; } } return false; } catch (MessagingException e) { return false; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/AndTerm.java0000664000175000017500000000536111345370365025470 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.util.Arrays; import javax.mail.Message; /** * Term that implements a logical AND across terms. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class AndTerm extends SearchTerm { private static final long serialVersionUID = -3583274505380989582L; /** * Terms to which the AND operator should be applied. */ protected SearchTerm[] terms; /** * Constructor for performing a binary AND. * * @param a the first term * @param b the second ter, */ public AndTerm(SearchTerm a, SearchTerm b) { terms = new SearchTerm[]{a, b}; } /** * Constructor for performing and AND across an arbitraty number of terms. * @param terms the terms to AND together */ public AndTerm(SearchTerm[] terms) { this.terms = terms; } /** * Return the terms. * @return the terms */ public SearchTerm[] getTerms() { return terms; } /** * Match by applying the terms, in order, to the Message and performing an AND operation * to the result. Comparision will stop immediately if one of the terms returns false. * * @param message the Message to apply the terms to * @return true if all terms match */ public boolean match(Message message) { for (int i = 0; i < terms.length; i++) { SearchTerm term = terms[i]; if (!term.match(message)) { return false; } } return true; } public boolean equals(Object other) { if (other == this) return true; if (other instanceof AndTerm == false) return false; return Arrays.equals(terms, ((AndTerm) other).terms); } public int hashCode() { int hash = 0; for (int i = 0; i < terms.length; i++) { hash = hash * 37 + terms[i].hashCode(); } return hash; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/FlagTerm.java0000664000175000017500000000601311345370365025632 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Flags; import javax.mail.Message; import javax.mail.MessagingException; /** * Term for matching message {@link Flags}. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class FlagTerm extends SearchTerm { private static final long serialVersionUID = -142991500302030647L; /** * If true, test that all flags are set; if false, test that all flags are clear. */ protected boolean set; /** * The flags to test. */ protected Flags flags; /** * @param flags the flags to test * @param set test for set or clear; {@link #set} */ public FlagTerm(Flags flags, boolean set) { this.set = set; this.flags = flags; } public Flags getFlags() { return flags; } public boolean getTestSet() { return set; } public boolean match(Message message) { try { Flags msgFlags = message.getFlags(); if (set) { return msgFlags.contains(flags); } else { // yuk - I wish we could get at the internal state of the Flags Flags.Flag[] system = flags.getSystemFlags(); for (int i = 0; i < system.length; i++) { Flags.Flag flag = system[i]; if (msgFlags.contains(flag)) { return false; } } String[] user = flags.getUserFlags(); for (int i = 0; i < user.length; i++) { String flag = user[i]; if (msgFlags.contains(flag)) { return false; } } return true; } } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof FlagTerm == false) return false; final FlagTerm otherFlags = (FlagTerm) other; return otherFlags.set == this.set && otherFlags.flags.equals(flags); } public int hashCode() { return set ? flags.hashCode() : ~flags.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/AddressStringTerm.java0000664000175000017500000000305111345370365027534 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; /** * A Term that compares two Addresses as Strings. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class AddressStringTerm extends StringTerm { private static final long serialVersionUID = 3086821234204980368L; /** * Constructor. * @param pattern the pattern to be compared */ protected AddressStringTerm(String pattern) { super(pattern); } /** * Tests if the patterm associated with this Term is a substring of * the address in the supplied object. * * @param address * @return */ protected boolean match(Address address) { return match(address.toString()); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/OrTerm.java0000664000175000017500000000374611345370365025353 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.util.Arrays; import javax.mail.Message; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class OrTerm extends SearchTerm { private static final long serialVersionUID = 5380534067523646936L; protected SearchTerm[] terms; public OrTerm(SearchTerm a, SearchTerm b) { terms = new SearchTerm[]{a, b}; } public OrTerm(SearchTerm[] terms) { this.terms = terms; } public SearchTerm[] getTerms() { return terms; } public boolean match(Message message) { for (int i = 0; i < terms.length; i++) { SearchTerm term = terms[i]; if (term.match(message)) { return true; } } return false; } public boolean equals(Object other) { if (other == this) return true; if (other instanceof OrTerm == false) return false; return Arrays.equals(terms, ((OrTerm) other).terms); } public int hashCode() { int hash = 0; for (int i = 0; i < terms.length; i++) { hash = hash * 37 + terms[i].hashCode(); } return hash; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/DateTerm.java0000664000175000017500000000435511345370365025645 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.util.Date; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class DateTerm extends ComparisonTerm { private static final long serialVersionUID = 4818873430063720043L; protected Date date; protected DateTerm(int comparison, Date date) { super(); this.comparison = comparison; this.date = date; } public Date getDate() { return date; } public int getComparison() { return comparison; } protected boolean match(Date match) { long matchTime = match.getTime(); long mytime = date.getTime(); switch (comparison) { case EQ: return matchTime == mytime; case NE: return matchTime != mytime; case LE: return matchTime <= mytime; case LT: return matchTime < mytime; case GT: return matchTime > mytime; case GE: return matchTime >= mytime; default: return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof DateTerm == false) return false; final DateTerm term = (DateTerm) other; return this.comparison == term.comparison && this.date.equals(term.date); } public int hashCode() { return date.hashCode() + super.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/AddressTerm.java0000664000175000017500000000402611345370365026350 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; /** * Term that compares two addresses. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class AddressTerm extends SearchTerm { private static final long serialVersionUID = 2005405551929769980L; /** * The address. */ protected Address address; /** * Constructor taking the address for this term. * @param address the address */ protected AddressTerm(Address address) { this.address = address; } /** * Return the address of this term. * * @return the addre4ss */ public Address getAddress() { return address; } /** * Match to the supplied address. * * @param address the address to match with * @return true if the addresses match */ protected boolean match(Address address) { return this.address.equals(address); } public boolean equals(Object other) { if (this == other) return true; if (other instanceof AddressTerm == false) return false; return address.equals(((AddressTerm) other).address); } public int hashCode() { return address.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/MessageIDTerm.java0000664000175000017500000000350511345370365026565 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class MessageIDTerm extends StringTerm { private static final long serialVersionUID = -2121096296454691963L; public MessageIDTerm(String id) { super(id); } public boolean match(Message message) { try { String values[] = message.getHeader("Message-ID"); if (values != null) { for (int i = 0; i < values.length; i++) { String value = values[i]; if (match(value)) { return true; } } } return false; } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (!(other instanceof MessageIDTerm)) { return false; } return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/NotTerm.java0000664000175000017500000000315311345370365025523 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; /** * Term that implements a logical negation. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class NotTerm extends SearchTerm { private static final long serialVersionUID = 7152293214217310216L; protected SearchTerm term; public NotTerm(SearchTerm term) { this.term = term; } public SearchTerm getTerm() { return term; } public boolean match(Message message) { return !term.match(message); } public boolean equals(Object other) { if (other == this) return true; if (other instanceof NotTerm == false) return false; return term.equals(((NotTerm) other).term); } public int hashCode() { return term.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/ComparisonTerm.java0000664000175000017500000000314711345370365027100 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; /** * Base for comparison terms. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class ComparisonTerm extends SearchTerm { private static final long serialVersionUID = 1456646953666474308L; public static final int LE = 1; public static final int LT = 2; public static final int EQ = 3; public static final int NE = 4; public static final int GT = 5; public static final int GE = 6; protected int comparison; public ComparisonTerm() { } public boolean equals(Object other) { if (!(other instanceof ComparisonTerm)) { return false; } return comparison == ((ComparisonTerm)other).comparison; } public int hashCode() { return comparison; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/SizeTerm.java0000664000175000017500000000306311345370365025675 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class SizeTerm extends IntegerComparisonTerm { private static final long serialVersionUID = -2556219451005103709L; public SizeTerm(int comparison, int size) { super(comparison, size); } public boolean match(Message message) { try { return match(message.getSize()); } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (this == other) return true; if (other instanceof SizeTerm == false) return false; return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/ReceivedDateTerm.java0000664000175000017500000000334011345370365027305 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import java.util.Date; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class ReceivedDateTerm extends DateTerm { private static final long serialVersionUID = -2756695246195503170L; public ReceivedDateTerm(int comparison, Date date) { super(comparison, date); } public boolean match(Message message) { try { Date date = message.getReceivedDate(); if (date == null) { return false; } return match(date); } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof ReceivedDateTerm == false) return false; return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/HeaderTerm.java0000664000175000017500000000433011345370365026151 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class HeaderTerm extends StringTerm { private static final long serialVersionUID = 8342514650333389122L; protected String headerName; public HeaderTerm(String header, String pattern) { super(pattern); this.headerName = header; } public String getHeaderName() { return headerName; } public boolean match(Message message) { try { String values[] = message.getHeader(headerName); if (values != null) { for (int i = 0; i < values.length; i++) { String value = values[i]; if (match(value)) { return true; } } } return false; } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof HeaderTerm == false) return false; // we need to compare with more than just the header name. return headerName.equalsIgnoreCase(((HeaderTerm) other).headerName) && super.equals(other); } public int hashCode() { return headerName.toLowerCase().hashCode() + super.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/IntegerComparisonTerm.java0000664000175000017500000000434311345370365030415 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; /** * A Term that provides comparisons for integers. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class IntegerComparisonTerm extends ComparisonTerm { private static final long serialVersionUID = -6963571240154302484L; protected int number; protected IntegerComparisonTerm(int comparison, int number) { super(); this.comparison = comparison; this.number = number; } public int getNumber() { return number; } public int getComparison() { return comparison; } protected boolean match(int match) { switch (comparison) { case EQ: return match == number; case NE: return match != number; case GT: return match > number; case GE: return match >= number; case LT: return match < number; case LE: return match <= number; default: return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof IntegerComparisonTerm == false) return false; final IntegerComparisonTerm term = (IntegerComparisonTerm) other; return this.comparison == term.comparison && this.number == term.number; } public int hashCode() { return number + super.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/SearchException.java0000664000175000017500000000231311345370365027214 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class SearchException extends MessagingException { private static final long serialVersionUID = -7092886778226268686L; public SearchException() { super(); } public SearchException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/MessageNumberTerm.java0000664000175000017500000000265111345370365027522 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Message; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class MessageNumberTerm extends IntegerComparisonTerm { private static final long serialVersionUID = -5379625829658623812L; public MessageNumberTerm(int number) { super(EQ, number); } public boolean match(Message message) { return match(message.getMessageNumber()); } public boolean equals(Object other) { if (!(other instanceof MessageNumberTerm)) { return false; } return super.equals(other); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/RecipientTerm.java0000664000175000017500000000437311345370365026712 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class RecipientTerm extends AddressTerm { private static final long serialVersionUID = 6548700653122680468L; protected Message.RecipientType type; public RecipientTerm(Message.RecipientType type, Address address) { super(address); this.type = type; } public Message.RecipientType getRecipientType() { return type; } public boolean match(Message message) { try { Address from[] = message.getRecipients(type); if (from == null) { return false; } for (int i = 0; i < from.length; i++) { Address address = from[i]; if (match(address)) { return true; } } return false; } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (this == other) return true; if (other instanceof RecipientTerm == false) return false; final RecipientTerm recipientTerm = (RecipientTerm) other; return address.equals(recipientTerm.address) && type == recipientTerm.type; } public int hashCode() { return address.hashCode() + type.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/StringTerm.java0000664000175000017500000000662411345370365026237 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; /** * A Term that provides matching criteria for Strings. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class StringTerm extends SearchTerm { private static final long serialVersionUID = 1274042129007696269L; /** * If true, case should be ignored during matching. */ protected boolean ignoreCase; /** * The pattern associated with this term. */ protected String pattern; /** * Constructor specifying a pattern. * Defaults to case insensitive matching. * @param pattern the pattern for this term */ protected StringTerm(String pattern) { this(pattern, true); } /** * Constructor specifying pattern and case sensitivity. * @param pattern the pattern for this term * @param ignoreCase if true, case should be ignored during matching */ protected StringTerm(String pattern, boolean ignoreCase) { this.pattern = pattern; this.ignoreCase = ignoreCase; } /** * Return the pattern associated with this term. * @return the pattern associated with this term */ public String getPattern() { return pattern; } /** * Indicate if case should be ignored when matching. * @return if true, case should be ignored during matching */ public boolean getIgnoreCase() { return ignoreCase; } /** * Determine if the pattern associated with this term is a substring of the * supplied String. If ignoreCase is true then case will be ignored. * * @param match the String to compare to * @return true if this patter is a substring of the supplied String */ protected boolean match(String match) { int matchLength = pattern.length(); int length = match.length() - matchLength; for (int i = 0; i <= length; i++) { if (match.regionMatches(ignoreCase, i, pattern, 0, matchLength)) { return true; } } return false; } public boolean equals(Object other) { if (this == other) return true; if (other instanceof StringTerm == false) return false; StringTerm term = (StringTerm)other; if (ignoreCase) { return term.pattern.equalsIgnoreCase(pattern) && term.ignoreCase == ignoreCase; } else { return term.pattern.equals(pattern) && term.ignoreCase == ignoreCase; } } public int hashCode() { return pattern.hashCode() + (ignoreCase ? 32 : 79); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/RecipientStringTerm.java0000664000175000017500000000443011345370365030073 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class RecipientStringTerm extends AddressStringTerm { private static final long serialVersionUID = -8293562089611618849L; private Message.RecipientType type; public RecipientStringTerm(Message.RecipientType type, String pattern) { super(pattern); this.type = type; } public Message.RecipientType getRecipientType() { return type; } public boolean match(Message message) { try { Address from[] = message.getRecipients(type); if (from == null) { return false; } for (int i = 0; i < from.length; i++) { Address address = from[i]; if (match(address)) { return true; } } return false; } catch (MessagingException e) { return false; } } public boolean equals(Object other) { if (other == this) return true; if (other instanceof RecipientStringTerm == false) return false; final RecipientStringTerm otherTerm = (RecipientStringTerm) other; return this.pattern.equals(otherTerm.pattern) && this.type == otherTerm.type; } public int hashCode() { return pattern.hashCode() + type.hashCode(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/search/FromTerm.java0000664000175000017500000000316711345370365025673 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.search; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public final class FromTerm extends AddressTerm { private static final long serialVersionUID = 5214730291502658665L; public FromTerm(Address match) { super(match); } public boolean match(Message message) { try { Address from[] = message.getFrom(); if (from == null) { return false; } for (int i = 0; i < from.length; i++) { if (match(from[i])) { return true; } } return false; } catch (MessagingException e) { return false; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Transport.java0000664000175000017500000002177310702410673024663 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import javax.mail.event.TransportEvent; import javax.mail.event.TransportListener; /** * Abstract class modeling a message transport. * * @version $Rev: 582780 $ $Date: 2007-10-08 07:17:15 -0400 (Mon, 08 Oct 2007) $ */ public abstract class Transport extends Service { /** * Send a message to all recipient addresses the message contains (as returned by {@link Message#getAllRecipients()}) * using message transports appropriate for each address. Message addresses are checked during submission, * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered. *

* {@link Message#saveChanges()} will be called before the message is actually sent. * * @param message the message to send * @throws MessagingException if there was a problem sending the message */ public static void send(Message message) throws MessagingException { send(message, message.getAllRecipients()); } /** * Send a message to all addresses provided irrespective of any recipients contained in the message, * using message transports appropriate for each address. Message addresses are checked during submission, * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered. *

* {@link Message#saveChanges()} will be called before the message is actually sent. * * @param message the message to send * @param addresses the addesses to send to * @throws MessagingException if there was a problem sending the message */ public static void send(Message message, Address[] addresses) throws MessagingException { Session session = message.session; Map msgsByTransport = new HashMap(); for (int i = 0; i < addresses.length; i++) { Address address = addresses[i]; Transport transport = session.getTransport(address); List addrs = (List) msgsByTransport.get(transport); if (addrs == null) { addrs = new ArrayList(); msgsByTransport.put(transport, addrs); } addrs.add(address); } message.saveChanges(); // Since we might be sending to multiple protocols, we need to catch and process each exception // when we send and then throw a new SendFailedException when everything is done. Unfortunately, this // also means unwrapping the information in any SendFailedExceptions we receive and building // composite failed list. MessagingException chainedException = null; ArrayList sentAddresses = new ArrayList(); ArrayList unsentAddresses = new ArrayList(); ArrayList invalidAddresses = new ArrayList(); for (Iterator i = msgsByTransport.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); Transport transport = (Transport) entry.getKey(); List addrs = (List) entry.getValue(); try { // we MUST connect to the transport before attempting to send. transport.connect(); transport.sendMessage(message, (Address[]) addrs.toArray(new Address[addrs.size()])); // if we have to throw an exception because of another failure, these addresses need to // be in the valid list. Since we succeeded here, we can add these now. sentAddresses.addAll(addrs); } catch (SendFailedException e) { // a true send failure. The exception contains a wealth of information about // the failures, including a potential chain of exceptions explaining what went wrong. We're // going to send a new one of these, so we need to merge the information. // add this to our exception chain if (chainedException == null) { chainedException = e; } else { chainedException.setNextException(e); } // now extract each of the address categories from Address[] exAddrs = e.getValidSentAddresses(); if (exAddrs != null) { for (int j = 0; j < exAddrs.length; j++) { sentAddresses.add(exAddrs[j]); } } exAddrs = e.getValidUnsentAddresses(); if (exAddrs != null) { for (int j = 0; j < exAddrs.length; j++) { unsentAddresses.add(exAddrs[j]); } } exAddrs = e.getInvalidAddresses(); if (exAddrs != null) { for (int j = 0; j < exAddrs.length; j++) { invalidAddresses.add(exAddrs[j]); } } } catch (MessagingException e) { // add this to our exception chain if (chainedException == null) { chainedException = e; } else { chainedException.setNextException(e); } } finally { transport.close(); } } // if we have an exception chain then we need to throw a new exception giving the failure // information. if (chainedException != null) { // if we're only sending to a single transport (common), and we received a SendFailedException // as a result, then we have a fully formed exception already. Rather than wrap this in another // exception, we can just rethrow the one we have. if (msgsByTransport.size() == 1 && chainedException instanceof SendFailedException) { throw chainedException; } // create our lists for notification and exception reporting from this point on. Address[] sent = (Address[])sentAddresses.toArray(new Address[0]); Address[] unsent = (Address[])unsentAddresses.toArray(new Address[0]); Address[] invalid = (Address[])invalidAddresses.toArray(new Address[0]); throw new SendFailedException("Send failure", chainedException, sent, unsent, invalid); } } /** * Constructor taking Session and URLName parameters required for {@link Service#Service(Session, URLName)}. * * @param session the Session this transport is for * @param name the location this transport is for */ public Transport(Session session, URLName name) { super(session, name); } /** * Send a message to the supplied addresses using this transport; if any of the addresses are * invalid then a {@link SendFailedException} is thrown. Whether the message is actually sent * to any of the addresses is undefined. *

* Unlike the static {@link #send(Message, Address[])} method, {@link Message#saveChanges()} is * not called. A {@link TransportEvent} will be sent to registered listeners once the delivery * attempt has been made. * * @param message the message to send * @param addresses list of addresses to send it to * @throws SendFailedException if the send failed * @throws MessagingException if there was a problem sending the message */ public abstract void sendMessage(Message message, Address[] addresses) throws MessagingException; private Vector transportListeners = new Vector(); public void addTransportListener(TransportListener listener) { transportListeners.add(listener); } public void removeTransportListener(TransportListener listener) { transportListeners.remove(listener); } protected void notifyTransportListeners(int type, Address[] validSent, Address[] validUnsent, Address[] invalid, Message message) { queueEvent(new TransportEvent(this, type, validSent, validUnsent, invalid, message), transportListeners); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Part.java0000664000175000017500000002522410675734274023607 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import javax.activation.DataHandler; /** * Note: Parts are used in Collections so implementing classes must provide * a suitable implementation of equals and hashCode. * * @version $Rev: 578802 $ $Date: 2007-09-24 09:16:44 -0400 (Mon, 24 Sep 2007) $ */ public interface Part { /** * This part should be presented as an attachment. */ public static final String ATTACHMENT = "attachment"; /** * This part should be presented or rendered inline. */ public static final String INLINE = "inline"; /** * Add this value to the existing headers with the given name. This method * does not replace any headers that may already exist. * * @param name The name of the target header. * @param value The value to be added to the header set. * * @exception MessagingException */ public abstract void addHeader(String name, String value) throws MessagingException; /** * Return all headers as an Enumeration of Header objects. * * @return An Enumeration containing all of the current Header objects. * @exception MessagingException */ public abstract Enumeration getAllHeaders() throws MessagingException; /** * Return a content object for this Part. The * content object type is dependent upon the * DataHandler for the Part. * * @return A content object for this Part. * @exception IOException * @exception MessagingException */ public abstract Object getContent() throws IOException, MessagingException; /** * Get the ContentType for this part, or null if the * ContentType has not been set. The ContentType * is expressed using the MIME typing system. * * @return The ContentType for this part. * @exception MessagingException */ public abstract String getContentType() throws MessagingException; /** * Returns a DataHandler instance for the content * with in the Part. * * @return A DataHandler appropriate for the Part content. * @exception MessagingException */ public abstract DataHandler getDataHandler() throws MessagingException; /** * Returns a description string for this Part. Returns * null if a description has not been set. * * @return The description string. * @exception MessagingException */ public abstract String getDescription() throws MessagingException; /** * Return the disposition of the part. The disposition * determines how the part should be presented to the * user. Two common disposition values are ATTACHMENT * and INLINE. * * @return The current disposition value. * @exception MessagingException */ public abstract String getDisposition() throws MessagingException; /** * Get a file name associated with this part. The * file name is useful for presenting attachment * parts as their original source. The file names * are generally simple names without containing * any directory information. Returns null if the * filename has not been set. * * @return The string filename, if any. * @exception MessagingException */ public abstract String getFileName() throws MessagingException; /** * Get all Headers for this header name. Returns null if no headers with * the given name exist. * * @param name The target header name. * * @return An array of all matching header values, or null if the given header * does not exist. * @exception MessagingException */ public abstract String[] getHeader(String name) throws MessagingException; /** * Return an InputStream for accessing the Part * content. Any mail-related transfer encodings * will be removed, so the data presented with * be the actual part content. * * @return An InputStream for accessing the part content. * @exception IOException * @exception MessagingException */ public abstract InputStream getInputStream() throws IOException, MessagingException; /** * Return the number of lines in the content, or * -1 if the line count cannot be determined. * * @return The estimated number of lines in the content. * @exception MessagingException */ public abstract int getLineCount() throws MessagingException; /** * Return all headers that match the list of names as an Enumeration of * Header objects. * * @param names An array of names of the desired headers. * * @return An Enumeration of Header objects containing the matching headers. * @exception MessagingException */ public abstract Enumeration getMatchingHeaders(String[] names) throws MessagingException; /** * Return an Enumeration of all Headers except those that match the names * given in the exclusion list. * * @param names An array of String header names that will be excluded from the return * Enumeration set. * * @return An Enumeration of Headers containing all headers except for those named * in the exclusion list. * @exception MessagingException */ public abstract Enumeration getNonMatchingHeaders(String[] names) throws MessagingException; /** * Return the size of this part, or -1 if the size * cannot be reliably determined. * * Note: the returned size does not take into account * internal encodings, nor is it an estimate of * how many bytes are required to transfer this * part across a network. This value is intended * to give email clients a rough idea of the amount * of space that might be required to present the * item. * * @return The estimated part size, or -1 if the size * information cannot be determined. * @exception MessagingException */ public abstract int getSize() throws MessagingException; /** * Tests if the part is of the specified MIME type. * Only the primaryPart and subPart of the MIME * type are used for the comparison; arguments are * ignored. The wildcard value of "*" may be used * to match all subTypes. * * @param mimeType The target MIME type. * * @return true if the part matches the input MIME type, * false if it is not of the requested type. * @exception MessagingException */ public abstract boolean isMimeType(String mimeType) throws MessagingException; /** * Remove all headers with the given name from the Part. * * @param name The target header name used for removal. * * @exception MessagingException */ public abstract void removeHeader(String name) throws MessagingException; public abstract void setContent(Multipart content) throws MessagingException; /** * Set a content object for this part. Internally, * the Part will use the MIME type encoded in the * type argument to wrap the provided content object. * In order for this to work properly, an appropriate * DataHandler must be installed in the Java Activation * Framework. * * @param content The content object. * @param type The MIME type for the inserted content Object. * * @exception MessagingException */ public abstract void setContent(Object content, String type) throws MessagingException; /** * Set a DataHandler for this part that defines the * Part content. The DataHandler is used to access * all Part content. * * @param handler The DataHandler instance. * * @exception MessagingException */ public abstract void setDataHandler(DataHandler handler) throws MessagingException; /** * Set a descriptive string for this part. * * @param description * The new description. * * @exception MessagingException */ public abstract void setDescription(String description) throws MessagingException; /** * Set the disposition for this Part. * * @param disposition * The disposition string. * * @exception MessagingException */ public abstract void setDisposition(String disposition) throws MessagingException; /** * Set a descriptive file name for this part. The * name should be a simple name that does not include * directory information. * * @param name The new name value. * * @exception MessagingException */ public abstract void setFileName(String name) throws MessagingException; /** * Sets a value for the given header. This operation will replace all * existing headers with the given name. * * @param name The name of the target header. * @param value The new value for the indicated header. * * @exception MessagingException */ public abstract void setHeader(String name, String value) throws MessagingException; /** * Set the Part content as text. This is a convenience method that sets * the content to a MIME type of "text/plain". * * @param content The new text content, as a String object. * * @exception MessagingException */ public abstract void setText(String content) throws MessagingException; /** * Write the Part content out to the provided OutputStream as a byte * stream using an encoding appropriate to the Part content. * * @param out The target OutputStream. * * @exception IOException * @exception MessagingException */ public abstract void writeTo(OutputStream out) throws IOException, MessagingException; } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/UIDFolder.java0000664000175000017500000000733010702421230024424 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 582797 $ $Date: 2007-10-08 08:29:12 -0400 (Mon, 08 Oct 2007) $ */ public interface UIDFolder { /** * A special value than can be passed as the end parameter to * {@link Folder#getMessages(int, int)} to indicate the last message in this folder. */ public static final long LASTUID = -1; /** * Get the UID validity value for this Folder. * * @return The current UID validity value, as a long. * @exception MessagingException */ public abstract long getUIDValidity() throws MessagingException; /** * Retrieve a message using the UID rather than the * message sequence number. Returns null if the message * doesn't exist. * * @param uid The target UID. * * @return the Message object. Returns null if the message does * not exist. * @exception MessagingException */ public abstract Message getMessageByUID(long uid) throws MessagingException; /** * Get a series of messages using a UID range. The * special value LASTUID can be used to mark the * last available message. * * @param start The start of the UID range. * @param end The end of the UID range. The special value * LASTUID can be used to request all messages up * to the last UID. * * @return An array containing all of the messages in the * range. * @exception MessagingException */ public abstract Message[] getMessagesByUID(long start, long end) throws MessagingException; /** * Retrieve a set of messages by explicit UIDs. If * any message in the list does not exist, null * will be returned for the corresponding item. * * @param ids An array of UID values to be retrieved. * * @return An array of Message items the same size as the ids * argument array. This array will contain null * entries for any UIDs that do not exist. * @exception MessagingException */ public abstract Message[] getMessagesByUID(long[] ids) throws MessagingException; /** * Retrieve the UID for a message from this Folder. * The argument Message MUST belong to this Folder * instance, otherwise a NoSuchElementException will * be thrown. * * @param message The target message. * * @return The UID associated with this message. * @exception MessagingException */ public abstract long getUID(Message message) throws MessagingException; /** * Special profile item used for fetching UID information. */ public static class FetchProfileItem extends FetchProfile.Item { public static final FetchProfileItem UID = new FetchProfileItem("UID"); protected FetchProfileItem(String name) { super(name); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Header.java0000664000175000017500000000331010517560657024056 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * Class representing a header field. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class Header { /** * The name of the header. */ protected String name; /** * The header value (can be null). */ protected String value; /** * Constructor initializing all immutable fields. * * @param name the name of this header * @param value the value of this header */ public Header(String name, String value) { this.name = name; this.value = value; } /** * Return the name of this header. * * @return the name of this header */ public String getName() { return name; } /** * Return the value of this header. * * @return the value of this header */ public String getValue() { return value; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/0000775000175000017500000000000011703375730023650 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimeMessage.java0000664000175000017500000016563111160451621026712 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectStreamException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.activation.DataHandler; import javax.mail.Address; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.internet.HeaderTokenizer.Token; import org.apache.geronimo.mail.util.ASCIIUtil; import org.apache.geronimo.mail.util.SessionUtil; /** * @version $Rev: 756013 $ $Date: 2009-03-19 10:15:45 -0400 (Thu, 19 Mar 2009) $ */ public class MimeMessage extends Message implements MimePart { private static final String MIME_ADDRESS_STRICT = "mail.mime.address.strict"; private static final String MIME_DECODEFILENAME = "mail.mime.decodefilename"; private static final String MIME_ENCODEFILENAME = "mail.mime.encodefilename"; private static final String MAIL_ALTERNATES = "mail.alternates"; private static final String MAIL_REPLYALLCC = "mail.replyallcc"; // static used to ensure message ID uniqueness private static int messageID = 0; /** * Extends {@link javax.mail.Message.RecipientType} to support addition recipient types. */ public static class RecipientType extends Message.RecipientType { /** * Recipient type for Usenet news. */ public static final RecipientType NEWSGROUPS = new RecipientType("Newsgroups"); protected RecipientType(String type) { super(type); } /** * Ensure the singleton is returned. * * @return resolved object */ protected Object readResolve() throws ObjectStreamException { if (this.type.equals("Newsgroups")) { return NEWSGROUPS; } else { return super.readResolve(); } } } /** * The {@link DataHandler} for this Message's content. */ protected DataHandler dh; /** * This message's content (unless sourced from a SharedInputStream). */ protected byte[] content; /** * If the data for this message was supplied by a {@link SharedInputStream} * then this is another such stream representing the content of this message; * if this field is non-null, then {@link #content} will be null. */ protected InputStream contentStream; /** * This message's headers. */ protected InternetHeaders headers; /** * This message's flags. */ protected Flags flags; /** * Flag indicating that the message has been modified; set to true when * an empty message is created or when {@link #saveChanges()} is called. */ protected boolean modified; /** * Flag indicating that the message has been saved. */ protected boolean saved; private final MailDateFormat dateFormat = new MailDateFormat(); /** * Create a new MimeMessage. * An empty message is created, with empty {@link #headers} and empty {@link #flags}. * The {@link #modified} flag is set. * * @param session the session for this message */ public MimeMessage(Session session) { super(session); headers = new InternetHeaders(); flags = new Flags(); // empty messages are modified, because the content is not there, and require saving before use. modified = true; saved = false; } /** * Create a MimeMessage by reading an parsing the data from the supplied stream. * * @param session the session for this message * @param in the stream to load from * @throws MessagingException if there is a problem reading or parsing the stream */ public MimeMessage(Session session, InputStream in) throws MessagingException { this(session); parse(in); // this message is complete, so marked as unmodified. modified = false; // and no saving required saved = true; } /** * Copy a MimeMessage. * * @param message the message to copy * @throws MessagingException is there was a problem copying the message */ public MimeMessage(MimeMessage message) throws MessagingException { super(message.session); // get a copy of the source message flags flags = message.getFlags(); // this is somewhat difficult to do. There's a lot of data in both the superclass and this // class that needs to undergo a "deep cloning" operation. These operations don't really exist // on the objects in question, so the only solution I can come up with is to serialize the // message data of the source object using the write() method, then reparse the data in this // object. I've not found a lot of uses for this particular constructor, so perhaps that's not // really all that bad of a solution. // serialize this out to an in-memory stream. ByteArrayOutputStream copy = new ByteArrayOutputStream(); try { // write this out the stream. message.writeTo(copy); copy.close(); // I think this ends up creating a new array for the data, but I'm not aware of any more // efficient options. ByteArrayInputStream inData = new ByteArrayInputStream(copy.toByteArray()); // now reparse this message into this object. inData.close(); parse (inData); // writing out the source data requires saving it, so we should consider this one saved also. saved = true; // this message is complete, so marked as unmodified. modified = false; } catch (IOException e) { // I'm not sure ByteArrayInput/OutputStream actually throws IOExceptions or not, but the method // signatures declare it, so we need to deal with it. Turning it into a messaging exception // should fit the bill. throw new MessagingException("Error copying MimeMessage data", e); } } /** * Create an new MimeMessage in the supplied {@link Folder} and message number. * * @param folder the Folder that contains the new message * @param number the message number of the new message */ protected MimeMessage(Folder folder, int number) { super(folder, number); headers = new InternetHeaders(); flags = new Flags(); // saving primarly involves updates to the message header. Since we're taking the header info // from a message store in this context, we mark the message as saved. saved = true; // we've not filled in the content yet, so this needs to be marked as modified modified = true; } /** * Create a MimeMessage by reading an parsing the data from the supplied stream. * * @param folder the folder for this message * @param in the stream to load from * @param number the message number of the new message * @throws MessagingException if there is a problem reading or parsing the stream */ protected MimeMessage(Folder folder, InputStream in, int number) throws MessagingException { this(folder, number); parse(in); // this message is complete, so marked as unmodified. modified = false; // and no saving required saved = true; } /** * Create a MimeMessage with the supplied headers and content. * * @param folder the folder for this message * @param headers the headers for the new message * @param content the content of the new message * @param number the message number of the new message * @throws MessagingException if there is a problem reading or parsing the stream */ protected MimeMessage(Folder folder, InternetHeaders headers, byte[] content, int number) throws MessagingException { this(folder, number); this.headers = headers; this.content = content; // this message is complete, so marked as unmodified. modified = false; } /** * Parse the supplied stream and initialize {@link #headers} and {@link #content} appropriately. * * @param in the stream to read * @throws MessagingException if there was a problem parsing the stream */ protected void parse(InputStream in) throws MessagingException { in = new BufferedInputStream(in); // create the headers first from the stream. Note: We need to do this // by calling createInternetHeaders because subclasses might wish to add // additional headers to the set initialized from the stream. headers = createInternetHeaders(in); // now we need to get the rest of the content as a byte array...this means reading from the current // position in the stream until the end and writing it to an accumulator ByteArrayOutputStream. ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { byte buffer[] = new byte[1024]; int count; while ((count = in.read(buffer, 0, 1024)) != -1) { baos.write(buffer, 0, count); } } catch (Exception e) { throw new MessagingException(e.toString(), e); } // and finally extract the content as a byte array. content = baos.toByteArray(); } /** * Get the message "From" addresses. This looks first at the * "From" headers, and no "From" header is found, the "Sender" * header is checked. Returns null if not found. * * @return An array of addresses identifying the message from target. Returns * null if this is not resolveable from the headers. * @exception MessagingException */ public Address[] getFrom() throws MessagingException { // strict addressing controls this. boolean strict = isStrictAddressing(); Address[] result = getHeaderAsInternetAddresses("From", strict); if (result == null) { result = getHeaderAsInternetAddresses("Sender", strict); } return result; } /** * Set the current message "From" recipient. This replaces any * existing "From" header. If the address is null, the header is * removed. * * @param address The new "From" target. * * @exception MessagingException */ public void setFrom(Address address) throws MessagingException { setHeader("From", address); } /** * Set the "From" header using the value returned by {@link InternetAddress#getLocalAddress(javax.mail.Session)}. * * @throws MessagingException if there was a problem setting the header */ public void setFrom() throws MessagingException { InternetAddress address = InternetAddress.getLocalAddress(session); // no local address resolvable? This is an error. if (address == null) { throw new MessagingException("No local address defined"); } setFrom(address); } /** * Add a set of addresses to the existing From header. * * @param addresses The list to add. * * @exception MessagingException */ public void addFrom(Address[] addresses) throws MessagingException { addHeader("From", addresses); } /** * Return the "Sender" header as an address. * * @return the "Sender" header as an address, or null if not present * @throws MessagingException if there was a problem parsing the header */ public Address getSender() throws MessagingException { Address[] addrs = getHeaderAsInternetAddresses("Sender", isStrictAddressing()); return addrs != null && addrs.length > 0 ? addrs[0] : null; } /** * Set the "Sender" header. If the address is null, this * will remove the current sender header. * * @param address the new Sender address * * @throws MessagingException * if there was a problem setting the header */ public void setSender(Address address) throws MessagingException { setHeader("Sender", address); } /** * Gets the recipients by type. Returns null if there are no * headers of the specified type. Acceptable RecipientTypes are: * * javax.mail.Message.RecipientType.TO * javax.mail.Message.RecipientType.CC * javax.mail.Message.RecipientType.BCC * javax.mail.internet.MimeMessage.RecipientType.NEWSGROUPS * * @param type The message RecipientType identifier. * * @return The array of addresses for the specified recipient types. * @exception MessagingException */ public Address[] getRecipients(Message.RecipientType type) throws MessagingException { // is this a NEWSGROUP request? We need to handle this as a special case here, because // this needs to return NewsAddress instances instead of InternetAddress items. if (type == RecipientType.NEWSGROUPS) { return getHeaderAsNewsAddresses(getHeaderForRecipientType(type)); } // the other types are all internet addresses. return getHeaderAsInternetAddresses(getHeaderForRecipientType(type), isStrictAddressing()); } /** * Retrieve all of the recipients defined for this message. This * returns a merged array of all possible message recipients * extracted from the headers. The relevant header types are: * * * javax.mail.Message.RecipientType.TO * javax.mail.Message.RecipientType.CC * javax.mail.Message.RecipientType.BCC * javax.mail.internet.MimeMessage.RecipientType.NEWSGROUPS * * @return An array of all target message recipients. * @exception MessagingException */ public Address[] getAllRecipients() throws MessagingException { List recipients = new ArrayList(); addRecipientsToList(recipients, RecipientType.TO); addRecipientsToList(recipients, RecipientType.CC); addRecipientsToList(recipients, RecipientType.BCC); addRecipientsToList(recipients, RecipientType.NEWSGROUPS); // this is supposed to return null if nothing is there. if (recipients.isEmpty()) { return null; } return (Address[]) recipients.toArray(new Address[recipients.size()]); } /** * Utility routine to merge different recipient types into a * single list. * * @param list The accumulator list. * @param type The recipient type to extract. * * @exception MessagingException */ private void addRecipientsToList(List list, Message.RecipientType type) throws MessagingException { Address[] recipients; if (type == RecipientType.NEWSGROUPS) { recipients = getHeaderAsNewsAddresses(getHeaderForRecipientType(type)); } else { recipients = getHeaderAsInternetAddresses(getHeaderForRecipientType(type), isStrictAddressing()); } if (recipients != null) { list.addAll(Arrays.asList(recipients)); } } /** * Set a recipients list for a particular recipient type. If the * list is null, the corresponding header is removed. * * @param type The type of recipient to set. * @param addresses The list of addresses. * * @exception MessagingException */ public void setRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException { setHeader(getHeaderForRecipientType(type), addresses); } /** * Set a recipient field to a string address (which may be a * list or group type). * * If the address is null, the field is removed. * * @param type The type of recipient to set. * @param address The address string. * * @exception MessagingException */ public void setRecipients(Message.RecipientType type, String address) throws MessagingException { setOrRemoveHeader(getHeaderForRecipientType(type), address); } /** * Add a list of addresses to a target recipient list. * * @param type The target recipient type. * @param address An array of addresses to add. * * @exception MessagingException */ public void addRecipients(Message.RecipientType type, Address[] address) throws MessagingException { addHeader(getHeaderForRecipientType(type), address); } /** * Add an address to a target recipient list by string name. * * @param type The target header type. * @param address The address to add. * * @exception MessagingException */ public void addRecipients(Message.RecipientType type, String address) throws MessagingException { addHeader(getHeaderForRecipientType(type), address); } /** * Get the ReplyTo address information. The headers are parsed * using the "mail.mime.address.strict" setting. If the "Reply-To" header does * not have any addresses, then the value of the "From" field is used. * * @return An array of addresses obtained from parsing the header. * @exception MessagingException */ public Address[] getReplyTo() throws MessagingException { Address[] addresses = getHeaderAsInternetAddresses("Reply-To", isStrictAddressing()); if (addresses == null) { addresses = getFrom(); } return addresses; } /** * Set the Reply-To field to the provided list of addresses. If * the address list is null, the header is removed. * * @param address The new field value. * * @exception MessagingException */ public void setReplyTo(Address[] address) throws MessagingException { setHeader("Reply-To", address); } /** * Returns the value of the "Subject" header. If the subject * is encoded as an RFC 2047 value, the value is decoded before * return. If decoding fails, the raw string value is * returned. * * @return The String value of the subject field. * @exception MessagingException */ public String getSubject() throws MessagingException { String subject = getSingleHeader("Subject"); if (subject == null) { return null; } else { try { // this needs to be unfolded before decodeing. return MimeUtility.decodeText(MimeUtility.unfold(subject)); } catch (UnsupportedEncodingException e) { // ignored. } } return subject; } /** * Set the value for the "Subject" header. If the subject * contains non US-ASCII characters, it is encoded in RFC 2047 * fashion. * * If the subject value is null, the Subject field is removed. * * @param subject The new subject value. * * @exception MessagingException */ public void setSubject(String subject) throws MessagingException { // just set this using the default character set. setSubject(subject, null); } public void setSubject(String subject, String charset) throws MessagingException { // standard null removal (yada, yada, yada....) if (subject == null) { removeHeader("Subject"); } else { try { String s = MimeUtility.fold(9, MimeUtility.encodeText(subject, charset, null)); // encode this, and then fold to fit the line lengths. setHeader("Subject", MimeUtility.fold(9, MimeUtility.encodeText(subject, charset, null))); } catch (UnsupportedEncodingException e) { throw new MessagingException("Encoding error", e); } } } /** * Get the value of the "Date" header field. Returns null if * if the field is absent or the date is not in a parseable format. * * @return A Date object parsed according to RFC 822. * @exception MessagingException */ public Date getSentDate() throws MessagingException { String value = getSingleHeader("Date"); if (value == null) { return null; } try { return dateFormat.parse(value); } catch (java.text.ParseException e) { return null; } } /** * Set the message sent date. This updates the "Date" header. * If the provided date is null, the header is removed. * * @param sent The new sent date value. * * @exception MessagingException */ public void setSentDate(Date sent) throws MessagingException { setOrRemoveHeader("Date", dateFormat.format(sent)); } /** * Get the message received date. The Sun implementation is * documented as always returning null, so this one does too. * * @return Always returns null. * @exception MessagingException */ public Date getReceivedDate() throws MessagingException { return null; } /** * Return the content size of this message. This is obtained * either from the size of the content field (if available) or * from the contentStream, IFF the contentStream returns a positive * size. Returns -1 if the size is not available. * * @return Size of the content in bytes. * @exception MessagingException */ public int getSize() throws MessagingException { if (content != null) { return content.length; } if (contentStream != null) { try { int size = contentStream.available(); if (size > 0) { return size; } } catch (IOException e) { // ignore } } return -1; } /** * Retrieve the line count for the current message. Returns * -1 if the count cannot be determined. * * The Sun implementation always returns -1, so this version * does too. * * @return The content line count (always -1 in this implementation). * @exception MessagingException */ public int getLineCount() throws MessagingException { return -1; } /** * Returns the current content type (defined in the "Content-Type" * header. If not available, "text/plain" is the default. * * @return The String name of the message content type. * @exception MessagingException */ public String getContentType() throws MessagingException { String value = getSingleHeader("Content-Type"); if (value == null) { value = "text/plain"; } return value; } /** * Tests to see if this message has a mime-type match with the * given type name. * * @param type The tested type name. * * @return If this is a type match on the primary and secondare portion of the types. * @exception MessagingException */ public boolean isMimeType(String type) throws MessagingException { return new ContentType(getContentType()).match(type); } /** * Retrieve the message "Content-Disposition" header field. * This value represents how the part should be represented to * the user. * * @return The string value of the Content-Disposition field. * @exception MessagingException */ public String getDisposition() throws MessagingException { String disp = getSingleHeader("Content-Disposition"); if (disp != null) { return new ContentDisposition(disp).getDisposition(); } return null; } /** * Set a new dispostion value for the "Content-Disposition" field. * If the new value is null, the header is removed. * * @param disposition * The new disposition value. * * @exception MessagingException */ public void setDisposition(String disposition) throws MessagingException { if (disposition == null) { removeHeader("Content-Disposition"); } else { // the disposition has parameters, which we'll attempt to preserve in any existing header. String currentHeader = getSingleHeader("Content-Disposition"); if (currentHeader != null) { ContentDisposition content = new ContentDisposition(currentHeader); content.setDisposition(disposition); setHeader("Content-Disposition", content.toString()); } else { // set using the raw string. setHeader("Content-Disposition", disposition); } } } /** * Decode the Content-Transfer-Encoding header to determine * the transfer encoding type. * * @return The string name of the required encoding. * @exception MessagingException */ public String getEncoding() throws MessagingException { // this might require some parsing to sort out. String encoding = getSingleHeader("Content-Transfer-Encoding"); if (encoding != null) { // we need to parse this into ATOMs and other constituent parts. We want the first // ATOM token on the string. HeaderTokenizer tokenizer = new HeaderTokenizer(encoding, HeaderTokenizer.MIME); Token token = tokenizer.next(); while (token.getType() != Token.EOF) { // if this is an ATOM type, return it. if (token.getType() == Token.ATOM) { return token.getValue(); } } // not ATOMs found, just return the entire header value....somebody might be able to make sense of // this. return encoding; } // no header, nothing to return. return null; } /** * Retrieve the value of the "Content-ID" header. Returns null * if the header does not exist. * * @return The current header value or null. * @exception MessagingException */ public String getContentID() throws MessagingException { return getSingleHeader("Content-ID"); } public void setContentID(String cid) throws MessagingException { setOrRemoveHeader("Content-ID", cid); } public String getContentMD5() throws MessagingException { return getSingleHeader("Content-MD5"); } public void setContentMD5(String md5) throws MessagingException { setOrRemoveHeader("Content-MD5", md5); } public String getDescription() throws MessagingException { String description = getSingleHeader("Content-Description"); if (description != null) { try { // this could be both folded and encoded. Return this to usable form. return MimeUtility.decodeText(MimeUtility.unfold(description)); } catch (UnsupportedEncodingException e) { // ignore } } // return the raw version for any errors. return description; } public void setDescription(String description) throws MessagingException { setDescription(description, null); } public void setDescription(String description, String charset) throws MessagingException { if (description == null) { removeHeader("Content-Description"); } else { try { setHeader("Content-Description", MimeUtility.fold(21, MimeUtility.encodeText(description, charset, null))); } catch (UnsupportedEncodingException e) { throw new MessagingException(e.getMessage(), e); } } } public String[] getContentLanguage() throws MessagingException { return getHeader("Content-Language"); } public void setContentLanguage(String[] languages) throws MessagingException { if (languages == null) { removeHeader("Content-Language"); } else if (languages.length == 1) { setHeader("Content-Language", languages[0]); } else { StringBuffer buf = new StringBuffer(languages.length * 20); buf.append(languages[0]); for (int i = 1; i < languages.length; i++) { buf.append(',').append(languages[i]); } setHeader("Content-Language", buf.toString()); } } public String getMessageID() throws MessagingException { return getSingleHeader("Message-ID"); } public String getFileName() throws MessagingException { // see if there is a disposition. If there is, parse off the filename parameter. String disposition = getDisposition(); String filename = null; if (disposition != null) { filename = new ContentDisposition(disposition).getParameter("filename"); } // if there's no filename on the disposition, there might be a name parameter on a // Content-Type header. if (filename == null) { String type = getContentType(); if (type != null) { try { filename = new ContentType(type).getParameter("name"); } catch (ParseException e) { } } } // if we have a name, we might need to decode this if an additional property is set. if (filename != null && SessionUtil.getBooleanProperty(session, MIME_DECODEFILENAME, false)) { try { filename = MimeUtility.decodeText(filename); } catch (UnsupportedEncodingException e) { throw new MessagingException("Unable to decode filename", e); } } return filename; } public void setFileName(String name) throws MessagingException { // there's an optional session property that requests file name encoding...we need to process this before // setting the value. if (name != null && SessionUtil.getBooleanProperty(session, MIME_ENCODEFILENAME, false)) { try { name = MimeUtility.encodeText(name); } catch (UnsupportedEncodingException e) { throw new MessagingException("Unable to encode filename", e); } } // get the disposition string. String disposition = getDisposition(); // if not there, then this is an attachment. if (disposition == null) { disposition = Part.ATTACHMENT; } // now create a disposition object and set the parameter. ContentDisposition contentDisposition = new ContentDisposition(disposition); contentDisposition.setParameter("filename", name); // serialize this back out and reset. setDisposition(contentDisposition.toString()); } public InputStream getInputStream() throws MessagingException, IOException { return getDataHandler().getInputStream(); } protected InputStream getContentStream() throws MessagingException { if (contentStream != null) { return contentStream; } if (content != null) { return new ByteArrayInputStream(content); } else { throw new MessagingException("No content"); } } public InputStream getRawInputStream() throws MessagingException { return getContentStream(); } public synchronized DataHandler getDataHandler() throws MessagingException { if (dh == null) { dh = new DataHandler(new MimePartDataSource(this)); } return dh; } public Object getContent() throws MessagingException, IOException { return getDataHandler().getContent(); } public void setDataHandler(DataHandler handler) throws MessagingException { dh = handler; // if we have a handler override, then we need to invalidate any content // headers that define the types. This information will be derived from the // data heander unless subsequently overridden. removeHeader("Content-Type"); removeHeader("Content-Transfer-Encoding"); } public void setContent(Object content, String type) throws MessagingException { setDataHandler(new DataHandler(content, type)); } public void setText(String text) throws MessagingException { setText(text, null, "plain"); } public void setText(String text, String charset) throws MessagingException { setText(text, charset, "plain"); } public void setText(String text, String charset, String subtype) throws MessagingException { // we need to sort out the character set if one is not provided. if (charset == null) { // if we have non us-ascii characters here, we need to adjust this. if (!ASCIIUtil.isAscii(text)) { charset = MimeUtility.getDefaultMIMECharset(); } else { charset = "us-ascii"; } } setContent(text, "text/" + subtype + "; charset=" + MimeUtility.quote(charset, HeaderTokenizer.MIME)); } public void setContent(Multipart part) throws MessagingException { setDataHandler(new DataHandler(part, part.getContentType())); part.setParent(this); } public Message reply(boolean replyToAll) throws MessagingException { // create a new message in this session. MimeMessage reply = createMimeMessage(session); // get the header and add the "Re:" bit, if necessary. String newSubject = getSubject(); if (newSubject != null) { // check to see if it already begins with "Re: " (in any case). // Add one on if we don't have it yet. if (!newSubject.regionMatches(true, 0, "Re: ", 0, 4)) { newSubject = "Re: " + newSubject; } reply.setSubject(newSubject); } // if this message has a message ID, then add a In-Reply-To and References // header to the reply message String messageID = getSingleHeader("Message-ID"); if (messageID != null) { // this one is just set unconditionally reply.setHeader("In-Reply-To", messageID); // we might already have a references header. If so, then add our message id // on the the end String references = getSingleHeader("References"); if (references == null) { references = messageID; } else { references = references + " " + messageID; } // and this is a replacement for whatever might be there. reply.setHeader("References", MimeUtility.fold("References: ".length(), references)); } Address[] toRecipients = getReplyTo(); // set the target recipients the replyTo value reply.setRecipients(Message.RecipientType.TO, getReplyTo()); // need to reply to everybody? More things to add. if (replyToAll) { // when replying, we want to remove "duplicates" in the final list. HashMap masterList = new HashMap(); // reply to all implies add the local sender. Add this to the list if resolveable. InternetAddress localMail = InternetAddress.getLocalAddress(session); if (localMail != null) { masterList.put(localMail.getAddress(), localMail); } // see if we have some local aliases to deal with. String alternates = session.getProperty(MAIL_ALTERNATES); if (alternates != null) { // parse this string list and merge with our set. Address[] alternateList = InternetAddress.parse(alternates, false); mergeAddressList(masterList, alternateList); } // the master list now contains an a list of addresses we will exclude from // the addresses. From this point on, we're going to prune any additional addresses // against this list, AND add any new addresses to the list // now merge in the main recipients, and merge in the other recipents as well Address[] toList = pruneAddresses(masterList, getRecipients(Message.RecipientType.TO)); if (toList.length != 0) { // now check to see what sort of reply we've been asked to send. // if replying to all as a CC, then we need to add to the CC list, otherwise they are // TO recipients. if (SessionUtil.getBooleanProperty(session, MAIL_REPLYALLCC, false)) { reply.addRecipients(Message.RecipientType.CC, toList); } else { reply.addRecipients(Message.RecipientType.TO, toList); } } // and repeat for the CC list. toList = pruneAddresses(masterList, getRecipients(Message.RecipientType.CC)); if (toList.length != 0) { reply.addRecipients(Message.RecipientType.CC, toList); } // a news group list is separate from the normal addresses. We just take these recepients // asis without trying to prune duplicates. toList = getRecipients(RecipientType.NEWSGROUPS); if (toList != null && toList.length != 0) { reply.addRecipients(RecipientType.NEWSGROUPS, toList); } } // this is a bit of a pain. We can't set the flags here by specifying the system flag, we need to // construct a flag item instance inorder to set it. // this is an answered email. setFlags(new Flags(Flags.Flag.ANSWERED), true); // all done, return the constructed Message object. return reply; } /** * Merge a set of addresses into a master accumulator list, eliminating * duplicates. * * @param master The set of addresses we've accumulated so far. * @param list The list of addresses to merge in. */ private void mergeAddressList(Map master, Address[] list) { // make sure we have a list. if (list == null) { return; } for (int i = 0; i < list.length; i++) { InternetAddress address = (InternetAddress)list[i]; // if not in the master list already, add it now. if (!master.containsKey(address.getAddress())) { master.put(address.getAddress(), address); } } } /** * Prune a list of addresses against our master address list, * returning the "new" addresses. The master list will be * updated with this new set of addresses. * * @param master The master address list of addresses we've seen before. * @param list The new list of addresses to prune. * * @return An array of addresses pruned of any duplicate addresses. */ private Address[] pruneAddresses(Map master, Address[] list) { // return an empy array if we don't get an input list. if (list == null) { return new Address[0]; } // optimistically assume there are no addresses to eliminate (common). ArrayList prunedList = new ArrayList(list.length); for (int i = 0; i < list.length; i++) { InternetAddress address = (InternetAddress)list[i]; // if not in the master list, this is a new one. Add to both the master list and // the pruned list. if (!master.containsKey(address.getAddress())) { master.put(address.getAddress(), address); prunedList.add(address); } } // convert back to list form. return (Address[])prunedList.toArray(new Address[0]); } /** * Write the message out to a stream in RFC 822 format. * * @param out The target output stream. * * @exception MessagingException * @exception IOException */ public void writeTo(OutputStream out) throws MessagingException, IOException { writeTo(out, null); } /** * Write the message out to a target output stream, excluding the * specified message headers. * * @param out The target output stream. * @param ignoreHeaders * An array of header types to ignore. This can be null, which means * write out all headers. * * @exception MessagingException * @exception IOException */ public void writeTo(OutputStream out, String[] ignoreHeaders) throws MessagingException, IOException { // make sure everything is saved before we write if (!saved) { saveChanges(); } // write out the headers first headers.writeTo(out, ignoreHeaders); // add the separater between the headers and the data portion. out.write('\r'); out.write('\n'); // if the modfied flag, we don't have current content, so the data handler needs to // take care of writing this data out. if (modified) { OutputStream encoderStream = MimeUtility.encode(out, getEncoding()); dh.writeTo(encoderStream); encoderStream.flush(); } else { // if we have content directly, we can write this out now. if (content != null) { out.write(content); } else { // see if we can get a content stream for this message. We might have had one // explicitly set, or a subclass might override the get method to provide one. InputStream in = getContentStream(); byte[] buffer = new byte[8192]; int length = in.read(buffer); // copy the data stream-to-stream. while (length > 0) { out.write(buffer, 0, length); length = in.read(buffer); } in.close(); } } // flush any data we wrote out, but do not close the stream. That's the caller's duty. out.flush(); } /** * Retrieve all headers that match a given name. * * @param name The target name. * * @return The set of headers that match the given name. These headers * will be the decoded() header values if these are RFC 2047 * encoded. * @exception MessagingException */ public String[] getHeader(String name) throws MessagingException { return headers.getHeader(name); } /** * Get all headers that match a particular name, as a single string. * Individual headers are separated by the provided delimiter. If * the delimiter is null, only the first header is returned. * * @param name The source header name. * @param delimiter The delimiter string to be used between headers. If null, only * the first is returned. * * @return The headers concatenated as a single string. * @exception MessagingException */ public String getHeader(String name, String delimiter) throws MessagingException { return headers.getHeader(name, delimiter); } /** * Set a new value for a named header. * * @param name The name of the target header. * @param value The new value for the header. * * @exception MessagingException */ public void setHeader(String name, String value) throws MessagingException { headers.setHeader(name, value); } /** * Conditionally set or remove a named header. If the new value * is null, the header is removed. * * @param name The header name. * @param value The new header value. A null value causes the header to be * removed. * * @exception MessagingException */ private void setOrRemoveHeader(String name, String value) throws MessagingException { if (value == null) { headers.removeHeader(name); } else { headers.setHeader(name, value); } } /** * Add a new value to an existing header. The added value is * created as an additional header of the same type and value. * * @param name The name of the target header. * @param value The removed header. * * @exception MessagingException */ public void addHeader(String name, String value) throws MessagingException { headers.addHeader(name, value); } /** * Remove a header with the given name. * * @param name The name of the removed header. * * @exception MessagingException */ public void removeHeader(String name) throws MessagingException { headers.removeHeader(name); } /** * Retrieve the complete list of message headers, as an enumeration. * * @return An Enumeration of the message headers. * @exception MessagingException */ public Enumeration getAllHeaders() throws MessagingException { return headers.getAllHeaders(); } public Enumeration getMatchingHeaders(String[] names) throws MessagingException { return headers.getMatchingHeaders(names); } public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { return headers.getNonMatchingHeaders(names); } public void addHeaderLine(String line) throws MessagingException { headers.addHeaderLine(line); } public Enumeration getAllHeaderLines() throws MessagingException { return headers.getAllHeaderLines(); } public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException { return headers.getMatchingHeaderLines(names); } public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException { return headers.getNonMatchingHeaderLines(names); } /** * Return a copy the flags associated with this message. * * @return a copy of the flags for this message * @throws MessagingException if there was a problem accessing the Store */ public synchronized Flags getFlags() throws MessagingException { return (Flags) flags.clone(); } /** * Check whether the supplied flag is set. * The default implementation checks the flags returned by {@link #getFlags()}. * * @param flag the flags to check for * @return true if the flags is set * @throws MessagingException if there was a problem accessing the Store */ public synchronized boolean isSet(Flags.Flag flag) throws MessagingException { return flags.contains(flag); } /** * Set or clear a flag value. * * @param flag The set of flags to effect. * @param set The value to set the flag to (true or false). * * @exception MessagingException */ public synchronized void setFlags(Flags flag, boolean set) throws MessagingException { if (set) { flags.add(flag); } else { flags.remove(flag); } } /** * Saves any changes on this message. When called, the modified * and saved flags are set to true and updateHeaders() is called * to force updates. * * @exception MessagingException */ public void saveChanges() throws MessagingException { // setting modified invalidates the current content. modified = true; saved = true; // update message headers from the content. updateHeaders(); } /** * Update the internet headers so that they make sense. This * will attempt to make sense of the message content type * given the state of the content. * * @exception MessagingException */ protected void updateHeaders() throws MessagingException { DataHandler handler = getDataHandler(); try { // figure out the content type. If not set, we'll need to figure this out. String type = dh.getContentType(); // we might need to reconcile the content type and our explicitly set type String explicitType = getSingleHeader("Content-Type"); // parse this content type out so we can do matches/compares. ContentType content = new ContentType(type); // is this a multipart content? if (content.match("multipart/*")) { // the content is suppose to be a MimeMultipart. Ping it to update it's headers as well. try { MimeMultipart part = (MimeMultipart)handler.getContent(); part.updateHeaders(); } catch (ClassCastException e) { throw new MessagingException("Message content is not MimeMultipart", e); } } else if (!content.match("message/rfc822")) { // simple part, we need to update the header type information // if no encoding is set yet, figure this out from the data handler content. if (getSingleHeader("Content-Transfer-Encoding") == null) { setHeader("Content-Transfer-Encoding", MimeUtility.getEncoding(handler)); } // is a content type header set? Check the property to see if we need to set this. if (explicitType == null) { if (SessionUtil.getBooleanProperty(session, "MIME_MAIL_SETDEFAULTTEXTCHARSET", true)) { // is this a text type? Figure out the encoding and make sure it is set. if (content.match("text/*")) { // the charset should be specified as a parameter on the MIME type. If not there, // try to figure one out. if (content.getParameter("charset") == null) { String encoding = getEncoding(); // if we're sending this as 7-bit ASCII, our character set need to be // compatible. if (encoding != null && encoding.equalsIgnoreCase("7bit")) { content.setParameter("charset", "us-ascii"); } else { // get the global default. content.setParameter("charset", MimeUtility.getDefaultMIMECharset()); } // replace the original type string type = content.toString(); } } } } } // if we don't have a content type header, then create one. if (explicitType == null) { // get the disposition header, and if it is there, copy the filename parameter into the // name parameter of the type. String disp = getSingleHeader("Content-Disposition"); if (disp != null) { // parse up the string value of the disposition ContentDisposition disposition = new ContentDisposition(disp); // now check for a filename value String filename = disposition.getParameter("filename"); // copy and rename the parameter, if it exists. if (filename != null) { content.setParameter("name", filename); // set the header with the updated content type information. type = content.toString(); } } // if no header has been set, then copy our current type string (which may // have been modified above) setHeader("Content-Type", type); } // make sure we set the MIME version setHeader("MIME-Version", "1.0"); // new javamail 1.4 requirement. updateMessageID(); } catch (IOException e) { throw new MessagingException("Error updating message headers", e); } } /** * Create a new set of internet headers from the * InputStream * * @param in The header source. * * @return A new InternetHeaders object containing the * appropriate headers. * @exception MessagingException */ protected InternetHeaders createInternetHeaders(InputStream in) throws MessagingException { // internet headers has a constructor for just this purpose return new InternetHeaders(in); } /** * Convert a header into an array of NewsAddress items. * * @param header The name of the source header. * * @return The parsed array of addresses. * @exception MessagingException */ private Address[] getHeaderAsNewsAddresses(String header) throws MessagingException { // NB: We're using getHeader() here to allow subclasses an opportunity to perform lazy loading // of the headers. String mergedHeader = getHeader(header, ","); if (mergedHeader != null) { return NewsAddress.parse(mergedHeader); } return null; } private Address[] getHeaderAsInternetAddresses(String header, boolean strict) throws MessagingException { // NB: We're using getHeader() here to allow subclasses an opportunity to perform lazy loading // of the headers. String mergedHeader = getHeader(header, ","); if (mergedHeader != null) { return InternetAddress.parseHeader(mergedHeader, strict); } return null; } /** * Check to see if we require strict addressing on parsing * internet headers. * * @return The current value of the "mail.mime.address.strict" session * property, or true, if the property is not set. */ private boolean isStrictAddressing() { return SessionUtil.getBooleanProperty(session, MIME_ADDRESS_STRICT, true); } /** * Set a named header to the value of an address field. * * @param header The header name. * @param address The address value. If the address is null, the header is removed. * * @exception MessagingException */ private void setHeader(String header, Address address) throws MessagingException { if (address == null) { removeHeader(header); } else { setHeader(header, address.toString()); } } /** * Set a header to a list of addresses. * * @param header The header name. * @param addresses An array of addresses to set the header to. If null, the * header is removed. */ private void setHeader(String header, Address[] addresses) { if (addresses == null) { headers.removeHeader(header); } else { headers.setHeader(header, addresses); } } private void addHeader(String header, Address[] addresses) throws MessagingException { headers.addHeader(header, InternetAddress.toString(addresses)); } private String getHeaderForRecipientType(Message.RecipientType type) throws MessagingException { if (RecipientType.TO == type) { return "To"; } else if (RecipientType.CC == type) { return "Cc"; } else if (RecipientType.BCC == type) { return "Bcc"; } else if (RecipientType.NEWSGROUPS == type) { return "Newsgroups"; } else { throw new MessagingException("Unsupported recipient type: " + type.toString()); } } /** * Utility routine to get a header as a single string value * rather than an array of headers. * * @param name The name of the header. * * @return The single string header value. If multiple headers exist, * the additional ones are ignored. * @exception MessagingException */ private String getSingleHeader(String name) throws MessagingException { String[] values = getHeader(name); if (values == null || values.length == 0) { return null; } else { return values[0]; } } /** * Update the message identifier after headers have been updated. * * The default message id is composed of the following items: * * 1) A newly created object's hash code. * 2) A uniqueness counter * 3) The current time in milliseconds * 4) The string JavaMail * 5) The user's local address as returned by InternetAddress.getLocalAddress(). * * @exception MessagingException */ protected void updateMessageID() throws MessagingException { StringBuffer id = new StringBuffer(); id.append('<'); id.append(new Object().hashCode()); id.append('.'); id.append(messageID++); id.append(System.currentTimeMillis()); id.append('.'); id.append("JavaMail."); // get the local address and apply a suitable default. InternetAddress localAddress = InternetAddress.getLocalAddress(session); if (localAddress != null) { id.append(localAddress.getAddress()); } else { id.append("javamailuser@localhost"); } id.append('>'); setHeader("Message-ID", id.toString()); } /** * Method used to create a new MimeMessage instance. This method * is used whenever the MimeMessage class needs to create a new * Message instance (e.g, reply()). This method allows subclasses * to override the class of message that gets created or set * default values, if needed. * * @param session The session associated with this message. * * @return A newly create MimeMessage instance. * @throws javax.mail.MessagingException if the MimeMessage could not be created */ protected MimeMessage createMimeMessage(Session session) throws javax.mail.MessagingException { return new MimeMessage(session); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/ContentType.java0000664000175000017500000001033411026434562026765 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; // can be in the form major/minor; charset=jobby /** * @version $Rev: 669445 $ $Date: 2008-06-19 06:48:18 -0400 (Thu, 19 Jun 2008) $ */ public class ContentType { private ParameterList _list; private String _minor; private String _major; public ContentType() { // the Sun version makes everything null here. } public ContentType(String major, String minor, ParameterList list) { _major = major; _minor = minor; _list = list; } public ContentType(String type) throws ParseException { // get a token parser for the type information HeaderTokenizer tokenizer = new HeaderTokenizer(type, HeaderTokenizer.MIME); // get the first token, which must be an ATOM HeaderTokenizer.Token token = tokenizer.next(); if (token.getType() != HeaderTokenizer.Token.ATOM) { throw new ParseException("Invalid content type"); } _major = token.getValue(); // the MIME type must be major/minor token = tokenizer.next(); if (token.getType() != '/') { throw new ParseException("Invalid content type"); } // this must also be an atom. Content types are not permitted to be wild cards. token = tokenizer.next(); if (token.getType() != HeaderTokenizer.Token.ATOM) { throw new ParseException("Invalid content type"); } _minor = token.getValue(); // the remainder is parameters, which ParameterList will take care of parsing. String remainder = tokenizer.getRemainder(); if (remainder != null) { _list = new ParameterList(remainder); } } public String getPrimaryType() { return _major; } public String getSubType() { return _minor; } public String getBaseType() { return _major + "/" + _minor; } public String getParameter(String name) { return (_list == null ? null : _list.get(name)); } public ParameterList getParameterList() { return _list; } public void setPrimaryType(String major) { _major = major; } public void setSubType(String minor) { _minor = minor; } public void setParameter(String name, String value) { if (_list == null) { _list = new ParameterList(); } _list.set(name, value); } public void setParameterList(ParameterList list) { _list = list; } public String toString() { if (_major == null || _minor == null) { return null; } // We need to format this as if we're doing it to set into the Content-Type // header. So the parameter list gets added on as if the header name was // also included. String baseType = getBaseType(); if (_list != null) { baseType += _list.toString(baseType.length() + "Content-Type: ".length()); } return baseType; } public boolean match(ContentType other) { return _major.equalsIgnoreCase(other._major) && (_minor.equalsIgnoreCase(other._minor) || _minor.equals("*") || other._minor.equals("*")); } public boolean match(String contentType) { try { return match(new ContentType(contentType)); } catch (ParseException e) { return false; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/NewsAddress.java0000664000175000017500000001076011345370365026742 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import javax.mail.Address; /** * A representation of an RFC1036 Internet newsgroup address. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class NewsAddress extends Address { private static final long serialVersionUID = -4203797299824684143L; /** * The host for this newsgroup */ protected String host; /** * The name of this newsgroup */ protected String newsgroup; public NewsAddress() { } public NewsAddress(String newsgroup) { this.newsgroup = newsgroup; } public NewsAddress(String newsgroup, String host) { this.newsgroup = newsgroup; this.host = host; } /** * The type of this address; always "news". * @return "news" */ public String getType() { return "news"; } public void setNewsgroup(String newsgroup) { this.newsgroup = newsgroup; } public String getNewsgroup() { return newsgroup; } public void setHost(String host) { this.host = host; } public String getHost() { return host; } public String toString() { // Sun impl only appears to return the newsgroup name, no host. return newsgroup; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NewsAddress)) return false; final NewsAddress newsAddress = (NewsAddress) o; if (host != null ? !host.equals(newsAddress.host) : newsAddress.host != null) return false; if (newsgroup != null ? !newsgroup.equals(newsAddress.newsgroup) : newsAddress.newsgroup != null) return false; return true; } public int hashCode() { int result; result = (host != null ? host.toLowerCase().hashCode() : 0); result = 29 * result + (newsgroup != null ? newsgroup.hashCode() : 0); return result; } /** * Parse a comma-spearated list of addresses. * * @param addresses the list to parse * @return the array of extracted addresses * @throws AddressException if one of the addresses is invalid */ public static NewsAddress[] parse(String addresses) throws AddressException { List result = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(addresses, ","); while (tokenizer.hasMoreTokens()) { String address = tokenizer.nextToken().trim(); int index = address.indexOf('@'); if (index == -1) { result.add(new NewsAddress(address)); } else { String newsgroup = address.substring(0, index).trim(); String host = address.substring(index+1).trim(); result.add(new NewsAddress(newsgroup, host)); } } return (NewsAddress[]) result.toArray(new NewsAddress[result.size()]); } /** * Convert the supplied addresses to a comma-separated String. * If addresses is null, returns null; if empty, returns an empty string. * * @param addresses the addresses to convert * @return a comma-separated list of addresses */ public static String toString(Address[] addresses) { if (addresses == null) { return null; } if (addresses.length == 0) { return ""; } StringBuffer result = new StringBuffer(addresses.length * 32); result.append(addresses[0]); for (int i = 1; i < addresses.length; i++) { result.append(',').append(addresses[i].toString()); } return result.toString(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MailDateFormat.java0000664000175000017500000005075511345370365027361 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; /** * Formats ths date as specified by * draft-ietf-drums-msg-fmt-08 dated January 26, 2000 * which supercedes RFC822. *

*

* The format used is EEE, d MMM yyyy HH:mm:ss Z and * locale is always US-ASCII. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MailDateFormat extends SimpleDateFormat { private static final long serialVersionUID = -8148227605210628779L; public MailDateFormat() { super("EEE, d MMM yyyy HH:mm:ss Z (z)", Locale.US); } public StringBuffer format(Date date, StringBuffer buffer, FieldPosition position) { return super.format(date, buffer, position); } /** * Parse a Mail date into a Date object. This uses fairly * lenient rules for the format because the Mail standards * for dates accept multiple formats. * * @param string The input string. * @param position The position argument. * * @return The Date object with the information inside. */ public Date parse(String string, ParsePosition position) { MailDateParser parser = new MailDateParser(string, position); try { return parser.parse(isLenient()); } catch (ParseException e) { e.printStackTrace(); // just return a null for any parsing errors return null; } } /** * The calendar cannot be set * @param calendar * @throws UnsupportedOperationException */ public void setCalendar(Calendar calendar) { throw new UnsupportedOperationException(); } /** * The format cannot be set * @param format * @throws UnsupportedOperationException */ public void setNumberFormat(NumberFormat format) { throw new UnsupportedOperationException(); } // utility class for handling date parsing issues class MailDateParser { // our list of defined whitespace characters static final String whitespace = " \t\r\n"; // current parsing position int current; // our end parsing position int endOffset; // the date source string String source; // The parsing position. We update this as we move along and // also for any parsing errors ParsePosition pos; public MailDateParser(String source, ParsePosition pos) { this.source = source; this.pos = pos; // we start using the providing parsing index. this.current = pos.getIndex(); this.endOffset = source.length(); } /** * Parse the timestamp, returning a date object. * * @param lenient The lenient setting from the Formatter object. * * @return A Date object based off of parsing the date string. * @exception ParseException */ public Date parse(boolean lenient) throws ParseException { // we just skip over any next date format, which means scanning ahead until we // find the first numeric character locateNumeric(); // the day can be either 1 or two digits int day = parseNumber(1, 2); // step over the delimiter skipDateDelimiter(); // parse off the month (which is in character format) int month = parseMonth(); // step over the delimiter skipDateDelimiter(); // now pull of the year, which can be either 2-digit or 4-digit int year = parseYear(); // white space is required here skipRequiredWhiteSpace(); // accept a 1 or 2 digit hour int hour = parseNumber(1, 2); skipRequiredChar(':'); // the minutes must be two digit int minutes = parseNumber(2, 2); // the seconds are optional, but the ":" tells us if they are to // be expected. int seconds = 0; if (skipOptionalChar(':')) { seconds = parseNumber(2, 2); } // skip over the white space skipWhiteSpace(); // and finally the timezone information int offset = parseTimeZone(); // set the index of how far we've parsed this pos.setIndex(current); // create a calendar for creating the date Calendar greg = new GregorianCalendar(TimeZone.getTimeZone("GMT")); // we inherit the leniency rules greg.setLenient(lenient); greg.set(year, month, day, hour, minutes, seconds); // now adjust by the offset. This seems a little strange, but we // need to negate the offset because this is a UTC calendar, so we need to // apply the reverse adjustment. for example, for the EST timezone, the offset // value will be -300 (5 hours). If the time was 15:00:00, the UTC adjusted time // needs to be 20:00:00, so we subract -300 minutes. greg.add(Calendar.MINUTE, -offset); // now return this timestamp. return greg.getTime(); } /** * Skip over a position where there's a required value * expected. * * @param ch The required character. * * @exception ParseException */ private void skipRequiredChar(char ch) throws ParseException { if (current >= endOffset) { parseError("Delimiter '" + ch + "' expected"); } if (source.charAt(current) != ch) { parseError("Delimiter '" + ch + "' expected"); } current++; } /** * Skip over a position where iff the position matches the * character * * @param ch The required character. * * @return true if the character was there, false otherwise. * @exception ParseException */ private boolean skipOptionalChar(char ch) { if (current >= endOffset) { return false; } if (source.charAt(current) != ch) { return false; } current++; return true; } /** * Skip over any white space characters until we find * the next real bit of information. Will scan completely to the * end, if necessary. */ private void skipWhiteSpace() { while (current < endOffset) { // if this is not in the white space list, then success. if (whitespace.indexOf(source.charAt(current)) < 0) { return; } current++; } // everything used up, just return } /** * Skip over any non-white space characters until we find * either a whitespace char or the end of the data. */ private void skipNonWhiteSpace() { while (current < endOffset) { // if this is not in the white space list, then success. if (whitespace.indexOf(source.charAt(current)) >= 0) { return; } current++; } // everything used up, just return } /** * Skip over any white space characters until we find * the next real bit of information. Will scan completely to the * end, if necessary. */ private void skipRequiredWhiteSpace() throws ParseException { int start = current; while (current < endOffset) { // if this is not in the white space list, then success. if (whitespace.indexOf(source.charAt(current)) < 0) { // we must have at least one white space character if (start == current) { parseError("White space character expected"); } return; } current++; } // everything used up, just return, but make sure we had at least one // white space if (start == current) { parseError("White space character expected"); } } private void parseError(String message) throws ParseException { // we've got an error, set the index to the end. pos.setErrorIndex(current); throw new ParseException(message, current); } /** * Locate an expected numeric field. * * @exception ParseException */ private void locateNumeric() throws ParseException { while (current < endOffset) { // found a digit? we're done if (Character.isDigit(source.charAt(current))) { return; } current++; } // we've got an error, set the index to the end. parseError("Number field expected"); } /** * Parse out an expected numeric field. * * @param minDigits The minimum number of digits we expect in this filed. * @param maxDigits The maximum number of digits expected. Parsing will * stop at the first non-digit character. An exception will * be thrown if the field contained more than maxDigits * in it. * * @return The parsed numeric value. * @exception ParseException */ private int parseNumber(int minDigits, int maxDigits) throws ParseException { int start = current; int accumulator = 0; while (current < endOffset) { char ch = source.charAt(current); // if this is not a digit character, then quit if (!Character.isDigit(ch)) { break; } // add the digit value into the accumulator accumulator = accumulator * 10 + Character.digit(ch, 10); current++; } int fieldLength = current - start; if (fieldLength < minDigits || fieldLength > maxDigits) { parseError("Invalid number field"); } return accumulator; } /** * Skip a delimiter between the date portions of the * string. The IMAP internal date format uses "-", so * we either accept a single "-" or any number of white * space characters (at least one required). * * @exception ParseException */ private void skipDateDelimiter() throws ParseException { if (current >= endOffset) { parseError("Invalid date field delimiter"); } if (source.charAt(current) == '-') { current++; } else { // must be at least a single whitespace character skipRequiredWhiteSpace(); } } /** * Parse a character month name into the date month * offset. * * @return * @exception ParseException */ private int parseMonth() throws ParseException { if ((endOffset - current) < 3) { parseError("Invalid month"); } int monthOffset = 0; String month = source.substring(current, current + 3).toLowerCase(); if (month.equals("jan")) { monthOffset = 0; } else if (month.equals("feb")) { monthOffset = 1; } else if (month.equals("mar")) { monthOffset = 2; } else if (month.equals("apr")) { monthOffset = 3; } else if (month.equals("may")) { monthOffset = 4; } else if (month.equals("jun")) { monthOffset = 5; } else if (month.equals("jul")) { monthOffset = 6; } else if (month.equals("aug")) { monthOffset = 7; } else if (month.equals("sep")) { monthOffset = 8; } else if (month.equals("oct")) { monthOffset = 9; } else if (month.equals("nov")) { monthOffset = 10; } else if (month.equals("dec")) { monthOffset = 11; } else { parseError("Invalid month"); } // ok, this is valid. Update the position and return it current += 3; return monthOffset; } /** * Parse off a year field that might be expressed as * either 2 or 4 digits. * * @return The numeric value of the year. * @exception ParseException */ private int parseYear() throws ParseException { // the year is between 2 to 4 digits int year = parseNumber(2, 4); // the two digit years get some sort of adjustment attempted. if (year < 50) { year += 2000; } else if (year < 100) { year += 1990; } return year; } /** * Parse all of the different timezone options. * * @return The timezone offset. * @exception ParseException */ private int parseTimeZone() throws ParseException { if (current >= endOffset) { parseError("Missing time zone"); } // get the first non-blank. If this is a sign character, this // is a zone offset. char sign = source.charAt(current); if (sign == '-' || sign == '+') { // need to step over the sign character current++; // a numeric timezone is always a 4 digit number, but // expressed as minutes/seconds. I'm too lazy to write a // different parser that will bound on just a couple of characters, so // we'll grab this as a single value and adjust int zoneInfo = parseNumber(4, 4); int offset = (zoneInfo / 100) * 60 + (zoneInfo % 100); // negate this, if we have a negativeo offset if (sign == '-') { offset = -offset; } return offset; } else { // need to parse this out using the obsolete zone names. This will be // either a 3-character code (defined set), or a single character military // zone designation. int start = current; skipNonWhiteSpace(); String name = source.substring(start, current).toUpperCase(); if (name.length() == 1) { return militaryZoneOffset(name); } else if (name.length() <= 3) { return namedZoneOffset(name); } else { parseError("Invalid time zone"); } return 0; } } /** * Parse the obsolete mail timezone specifiers. The * allowed set of timezones are terribly US centric. * That's the spec. The preferred timezone form is * the +/-mmss form. * * @param name The input name. * * @return The standard timezone offset for the specifier. * @exception ParseException */ private int namedZoneOffset(String name) throws ParseException { // NOTE: This is "UT", NOT "UTC" if (name.equals("UT")) { return 0; } else if (name.equals("GMT")) { return 0; } else if (name.equals("EST")) { return -300; } else if (name.equals("EDT")) { return -240; } else if (name.equals("CST")) { return -360; } else if (name.equals("CDT")) { return -300; } else if (name.equals("MST")) { return -420; } else if (name.equals("MDT")) { return -360; } else if (name.equals("PST")) { return -480; } else if (name.equals("PDT")) { return -420; } else { parseError("Invalid time zone"); return 0; } } /** * Parse a single-character military timezone. * * @param name The one-character name. * * @return The offset corresponding to the military designation. */ private int militaryZoneOffset(String name) throws ParseException { switch (Character.toUpperCase(name.charAt(0))) { case 'A': return 60; case 'B': return 120; case 'C': return 180; case 'D': return 240; case 'E': return 300; case 'F': return 360; case 'G': return 420; case 'H': return 480; case 'I': return 540; case 'K': return 600; case 'L': return 660; case 'M': return 720; case 'N': return -60; case 'O': return -120; case 'P': return -180; case 'Q': return -240; case 'R': return -300; case 'S': return -360; case 'T': return -420; case 'U': return -480; case 'V': return -540; case 'W': return -600; case 'X': return -660; case 'Y': return -720; case 'Z': return 0; default: parseError("Invalid time zone"); return 0; } } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/PreencodedMimeBodyPart.java0000664000175000017500000000502010517560657031043 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.IOException; import java.io.OutputStream; import javax.mail.MessagingException; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class PreencodedMimeBodyPart extends MimeBodyPart { // the defined transfer encoding private String transferEncoding; /** * Create a new body part with the specified MIME transfer encoding. * * @param encoding The content encoding. */ public PreencodedMimeBodyPart(String encoding) { transferEncoding = encoding; } /** * Retieve the defined encoding for this body part. * * @return * @exception MessagingException */ public String getEncoding() throws MessagingException { return transferEncoding; } /** * Write the body part content to the stream without applying * and additional encodings. * * @param out The target output stream. * * @exception IOException * @exception MessagingException */ public void writeTo(OutputStream out) throws IOException, MessagingException { headers.writeTo(out, null); // add the separater between the headers and the data portion. out.write('\r'); out.write('\n'); // write this out without getting an encoding stream getDataHandler().writeTo(out); out.flush(); } /** * Override of update headers to ensure the transfer encoding * is forced to the correct type. * * @exception MessagingException */ protected void updateHeaders() throws MessagingException { super.updateHeaders(); setHeader("Content-Transfer-Encoding", transferEncoding); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/SharedInputStream.java0000664000175000017500000000211510517560657030122 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.InputStream; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface SharedInputStream { public abstract long getPosition(); public abstract InputStream newStream(long start, long end); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/AddressException.java0000664000175000017500000000324511345370365027764 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class AddressException extends ParseException { private static final long serialVersionUID = 9134583443539323120L; protected int pos; protected String ref; public AddressException() { this(null); } public AddressException(String message) { this(message, null); } public AddressException(String message, String ref) { this(message, null, -1); } public AddressException(String message, String ref, int pos) { super(message); this.ref = ref; this.pos = pos; } public String getRef() { return ref; } public int getPos() { return pos; } public String toString() { return super.toString() + " (" + ref + "," + pos + ")"; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/ParseException.java0000664000175000017500000000231111345370365027442 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import javax.mail.MessagingException; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class ParseException extends MessagingException { private static final long serialVersionUID = 7649991205183658089L; public ParseException() { super(); } public ParseException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimeMultipart.java0000664000175000017500000005710711404403313027301 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import javax.activation.DataSource; import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.MultipartDataSource; import org.apache.geronimo.mail.util.SessionUtil; /** * @version $Rev: 953633 $ $Date: 2010-06-11 05:50:03 -0400 (Fri, 11 Jun 2010) $ */ public class MimeMultipart extends Multipart { private static final String MIME_IGNORE_MISSING_BOUNDARY = "mail.mime.multipart.ignoremissingendboundary"; /** * DataSource that provides our InputStream. */ protected DataSource ds; /** * Indicates if the data has been parsed. */ protected boolean parsed = true; // the content type information private transient ContentType type; // indicates if we've seen the final boundary line when parsing. private boolean complete = true; // MIME multipart preable text that can appear before the first boundary line. private String preamble = null; /** * Create an empty MimeMultipart with content type "multipart/mixed" */ public MimeMultipart() { this("mixed"); } /** * Create an empty MimeMultipart with the subtype supplied. * * @param subtype the subtype */ public MimeMultipart(String subtype) { type = new ContentType("multipart", subtype, null); type.setParameter("boundary", getBoundary()); contentType = type.toString(); } /** * Create a MimeMultipart from the supplied DataSource. * * @param dataSource the DataSource to use * @throws MessagingException */ public MimeMultipart(DataSource dataSource) throws MessagingException { ds = dataSource; if (dataSource instanceof MultipartDataSource) { super.setMultipartDataSource((MultipartDataSource) dataSource); parsed = true; } else { // We keep the original, provided content type string so that we // don't end up changing quoting/formatting of the header unless // changes are made to the content type. James is somewhat dependent // on that behavior. contentType = ds.getContentType(); type = new ContentType(contentType); parsed = false; } } public void setSubType(String subtype) throws MessagingException { type.setSubType(subtype); contentType = type.toString(); } public int getCount() throws MessagingException { parse(); return super.getCount(); } public synchronized BodyPart getBodyPart(int part) throws MessagingException { parse(); return super.getBodyPart(part); } public BodyPart getBodyPart(String cid) throws MessagingException { parse(); for (int i = 0; i < parts.size(); i++) { MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i); if (cid.equals(bodyPart.getContentID())) { return bodyPart; } } return null; } protected void updateHeaders() throws MessagingException { parse(); for (int i = 0; i < parts.size(); i++) { MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i); bodyPart.updateHeaders(); } } private static byte[] dash = { '-', '-' }; private static byte[] crlf = { 13, 10 }; public void writeTo(OutputStream out) throws IOException, MessagingException { parse(); String boundary = type.getParameter("boundary"); byte[] bytes = boundary.getBytes("ISO8859-1"); if (preamble != null) { byte[] preambleBytes = preamble.getBytes("ISO8859-1"); // write this out, followed by a line break. out.write(preambleBytes); out.write(crlf); } for (int i = 0; i < parts.size(); i++) { BodyPart bodyPart = (BodyPart) parts.get(i); out.write(dash); out.write(bytes); out.write(crlf); bodyPart.writeTo(out); out.write(crlf); } out.write(dash); out.write(bytes); out.write(dash); out.write(crlf); out.flush(); } protected void parse() throws MessagingException { if (parsed) { return; } try { ContentType cType = new ContentType(contentType); InputStream is = new BufferedInputStream(ds.getInputStream()); BufferedInputStream pushbackInStream = null; String boundaryString = cType.getParameter("boundary"); byte[] boundary = null; if (boundaryString == null) { pushbackInStream = new BufferedInputStream(is, 1200); // read until we find something that looks like a boundary string boundary = readTillFirstBoundary(pushbackInStream); } else { boundary = ("--" + boundaryString).getBytes("ISO8859-1"); pushbackInStream = new BufferedInputStream(is, boundary.length + 1000); readTillFirstBoundary(pushbackInStream, boundary); } while (true) { MimeBodyPartInputStream partStream; partStream = new MimeBodyPartInputStream(pushbackInStream, boundary); addBodyPart(new MimeBodyPart(partStream)); // terminated by an EOF rather than a proper boundary? if (!partStream.boundaryFound) { if (!SessionUtil.getBooleanProperty(MIME_IGNORE_MISSING_BOUNDARY, true)) { throw new MessagingException("Missing Multi-part end boundary"); } complete = false; } // if we hit the final boundary, stop processing this if (partStream.finalBoundaryFound) { break; } } } catch (Exception e){ throw new MessagingException(e.toString(),e); } parsed = true; } /** * Move the read pointer to the begining of the first part * read till the end of first boundary. Any data read before this point are * saved as the preamble. * * @param pushbackInStream * @param boundary * @throws MessagingException */ private byte[] readTillFirstBoundary(BufferedInputStream pushbackInStream) throws MessagingException { ByteArrayOutputStream preambleStream = new ByteArrayOutputStream(); try { while (true) { // read the next line byte[] line = readLine(pushbackInStream); // hit an EOF? if (line == null) { throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary"); } // if this looks like a boundary, then make it so if (line.length > 2 && line[0] == '-' && line[1] == '-') { // save the preamble, if there is one. byte[] preambleBytes = preambleStream.toByteArray(); if (preambleBytes.length > 0) { preamble = new String(preambleBytes, "ISO8859-1"); } return stripLinearWhiteSpace(line); } else { // this is part of the preamble. preambleStream.write(line); preambleStream.write('\r'); preambleStream.write('\n'); } } } catch (IOException ioe) { throw new MessagingException(ioe.toString(), ioe); } } /** * Scan a line buffer stripping off linear whitespace * characters, returning a new array without the * characters, if possible. * * @param line The source line buffer. * * @return A byte array with white space characters removed, * if necessary. */ private byte[] stripLinearWhiteSpace(byte[] line) { int index = line.length - 1; // if the last character is not a space or tab, we // can use this unchanged if (line[index] != ' ' && line[index] != '\t') { return line; } // scan backwards for the first non-white space for (; index > 0; index--) { if (line[index] != ' ' && line[index] != '\t') { break; } } // make a shorter copy of this byte[] newLine = new byte[index + 1]; System.arraycopy(line, 0, newLine, 0, index + 1); return newLine; } /** * Move the read pointer to the begining of the first part * read till the end of first boundary. Any data read before this point are * saved as the preamble. * * @param pushbackInStream * @param boundary * @throws MessagingException */ private void readTillFirstBoundary(BufferedInputStream pushbackInStream, byte[] boundary) throws MessagingException { ByteArrayOutputStream preambleStream = new ByteArrayOutputStream(); try { while (true) { // read the next line byte[] line = readLine(pushbackInStream); // hit an EOF? if (line == null) { throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary"); } // apply the boundary comparison rules to this if (compareBoundary(line, boundary)) { // save the preamble, if there is one. byte[] preambleBytes = preambleStream.toByteArray(); if (preambleBytes.length > 0) { preamble = new String(preambleBytes, "ISO8859-1"); } return; } // this is part of the preamble. preambleStream.write(line); preambleStream.write('\r'); preambleStream.write('\n'); } } catch (IOException ioe) { throw new MessagingException(ioe.toString(), ioe); } } /** * Peform a boundary comparison, taking into account * potential linear white space * * @param line The line to compare. * @param boundary The boundary we're searching for * * @return true if this is a valid boundary line, false for * any mismatches. */ private boolean compareBoundary(byte[] line, byte[] boundary) { // if the line is too short, this is an easy failure if (line.length < boundary.length) { return false; } // this is the most common situation if (line.length == boundary.length) { return Arrays.equals(line, boundary); } // the line might have linear white space after the boundary portions for (int i = 0; i < boundary.length; i++) { // fail on any mismatch if (line[i] != boundary[i]) { return false; } } // everything after the boundary portion must be linear whitespace for (int i = boundary.length; i < line.length; i++) { // fail on any mismatch if (line[i] != ' ' && line[i] != '\t') { return false; } } // these are equivalent return true; } /** * Read a single line of data from the input stream, * returning it as an array of bytes. * * @param in The source input stream. * * @return A byte array containing the line data. Returns * null if there's nothing left in the stream. * @exception MessagingException */ private byte[] readLine(BufferedInputStream in) throws IOException { ByteArrayOutputStream line = new ByteArrayOutputStream(); while (in.available() > 0) { int value = in.read(); if (value == -1) { // if we have nothing in the accumulator, signal an EOF back if (line.size() == 0) { return null; } break; } else if (value == '\r') { in.mark(10); value = in.read(); // we expect to find a linefeed after the carriage return, but // some things play loose with the rules. if (value != '\n') { in.reset(); } break; } else if (value == '\n') { // naked linefeed, allow that break; } else { // write this to the line line.write((byte)value); } } // return this as an array of bytes return line.toByteArray(); } protected InternetHeaders createInternetHeaders(InputStream in) throws MessagingException { return new InternetHeaders(in); } protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] data) throws MessagingException { return new MimeBodyPart(headers, data); } protected MimeBodyPart createMimeBodyPart(InputStream in) throws MessagingException { return new MimeBodyPart(in); } // static used to track boudary value allocations to help ensure uniqueness. private static int part; private synchronized static String getBoundary() { int i; synchronized(MimeMultipart.class) { i = part++; } StringBuffer buf = new StringBuffer(64); buf.append("----=_Part_").append(i).append('_').append((new Object()).hashCode()).append('.').append(System.currentTimeMillis()); return buf.toString(); } private class MimeBodyPartInputStream extends InputStream { BufferedInputStream inStream; public boolean boundaryFound = false; byte[] boundary; public boolean finalBoundaryFound = false; public MimeBodyPartInputStream(BufferedInputStream inStream, byte[] boundary) { super(); this.inStream = inStream; this.boundary = boundary; } /** * The base reading method for reading one character * at a time. * * @return The read character, or -1 if an EOF was encountered. * @exception IOException */ public int read() throws IOException { if (boundaryFound) { return -1; } // read the next value from stream int firstChar = inStream.read(); // premature end? Handle it like a boundary located if (firstChar == -1) { boundaryFound = true; // also mark this as the end finalBoundaryFound = true; return -1; } // we first need to look for a line boundary. If we find a boundary, it can be followed by the // boundary marker, so we need to remember what sort of thing we found, then read ahead looking // for the part boundary. // NB:, we only handle [\r]\n--boundary marker[--] // we need to at least accept what most mail servers would consider an // invalid format using just '\n' if (firstChar != '\r' && firstChar != '\n') { // not a \r, just return the byte as is return firstChar; } // we might need to rewind to this point. The padding is to allow for // line terminators and linear whitespace on the boundary lines inStream.mark(boundary.length + 1000); // we need to keep track of the first read character in case we need to // rewind back to the mark point int value = firstChar; // if this is a '\r', then we require the '\n' if (value == '\r') { // now scan ahead for the second character value = inStream.read(); if (value != '\n') { // only a \r, so this can't be a boundary. Return the // \r as if it was data, after first resetting inStream.reset(); return '\r'; } } value = inStream.read(); // if the next character is not a boundary start, we // need to handle this as a normal line end if ((byte) value != boundary[0]) { // just reset and return the first character as data inStream.reset(); return firstChar; } // we're here because we found a "\r\n-" sequence, which is a potential // boundary marker. Read the individual characters of the next line until // we have a mismatch // read value is the first byte of the boundary. Start matching the // next characters to find a boundary int boundaryIndex = 0; while ((boundaryIndex < boundary.length) && ((byte) value == boundary[boundaryIndex])) { value = inStream.read(); boundaryIndex++; } // if we didn't match all the way, we need to push back what we've read and // return the EOL character if (boundaryIndex != boundary.length) { // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } // The full boundary sequence should be \r\n--boundary string[--]\r\n // if the last character we read was a '-', check for the end terminator if (value == '-') { value = inStream.read(); // crud, we have a bad boundary terminator. We need to unwind this all the way // back to the lineend and pretend none of this ever happened if (value != '-') { // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } // on the home stretch, but we need to verify the LWSP/EOL sequence value = inStream.read(); // first skip over the linear whitespace while (value == ' ' || value == '\t') { value = inStream.read(); } // We've matched the final boundary, skipped any whitespace, but // we've hit the end of the stream. This is highly likely when // we have nested multiparts, since the linend terminator for the // final boundary marker is eated up as the start of the outer // boundary marker. No CRLF sequence here is ok. if (value == -1) { // we've hit the end of times... finalBoundaryFound = true; // we have a boundary, so return this as an EOF condition boundaryFound = true; return -1; } // this must be a CR or a LF...which leaves us even more to push back and forget if (value != '\r' && value != '\n') { // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } // if this is carriage return, check for a linefeed if (value == '\r') { // last check, this must be a line feed value = inStream.read(); if (value != '\n') { // SO CLOSE! // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } } // we've hit the end of times... finalBoundaryFound = true; } else { // first skip over the linear whitespace while (value == ' ' || value == '\t') { value = inStream.read(); } // this must be a CR or a LF...which leaves us even more to push back and forget if (value != '\r' && value != '\n') { // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } // if this is carriage return, check for a linefeed if (value == '\r') { // last check, this must be a line feed value = inStream.read(); if (value != '\n') { // SO CLOSE! // Boundary not found. Restoring bytes skipped. // just reset and return the first character as data inStream.reset(); return firstChar; } } } // we have a boundary, so return this as an EOF condition boundaryFound = true; return -1; } } /** * Return true if the final boundary line for this multipart was * seen when parsing the data. * * @return * @exception MessagingException */ public boolean isComplete() throws MessagingException { // make sure we've parsed this parse(); return complete; } /** * Returns the preamble text that appears before the first bady * part of a MIME multi part. The preamble is optional, so this * might be null. * * @return The preamble text string. * @exception MessagingException */ public String getPreamble() throws MessagingException { parse(); return preamble; } /** * Set the message preamble text. This will be written before * the first boundary of a multi-part message. * * @param preamble The new boundary text. This is complete lines of text, including * new lines. * * @exception MessagingException */ public void setPreamble(String preamble) throws MessagingException { this.preamble = preamble; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimeUtility.java0000664000175000017500000015463010754633212026774 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.StringTokenizer; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.MessagingException; import org.apache.geronimo.mail.util.ASCIIUtil; import org.apache.geronimo.mail.util.Base64; import org.apache.geronimo.mail.util.Base64DecoderStream; import org.apache.geronimo.mail.util.Base64Encoder; import org.apache.geronimo.mail.util.Base64EncoderStream; import org.apache.geronimo.mail.util.QuotedPrintableDecoderStream; import org.apache.geronimo.mail.util.QuotedPrintableEncoderStream; import org.apache.geronimo.mail.util.QuotedPrintableEncoder; import org.apache.geronimo.mail.util.QuotedPrintable; import org.apache.geronimo.mail.util.SessionUtil; import org.apache.geronimo.mail.util.UUDecoderStream; import org.apache.geronimo.mail.util.UUEncoderStream; // encodings include "base64", "quoted-printable", "7bit", "8bit" and "binary". // In addition, "uuencode" is also supported. The /** * @version $Rev: 627556 $ $Date: 2008-02-13 13:27:22 -0500 (Wed, 13 Feb 2008) $ */ public class MimeUtility { private static final String MIME_FOLDENCODEDWORDS = "mail.mime.foldencodedwords"; private static final String MIME_DECODE_TEXT_STRICT = "mail.mime.decodetext.strict"; private static final String MIME_FOLDTEXT = "mail.mime.foldtext"; private static final int FOLD_THRESHOLD = 76; private MimeUtility() { } public static final int ALL = -1; private static String defaultJavaCharset; private static String escapedChars = "\"\\\r\n"; private static String linearWhiteSpace = " \t\r\n"; private static String QP_WORD_SPECIALS = "=_?\"#$%&'(),.:;<>@[\\]^`{|}~"; private static String QP_TEXT_SPECIALS = "=_?"; // the javamail spec includes the ability to map java encoding names to MIME-specified names. Normally, // these values are loaded from a character mapping file. private static Map java2mime; private static Map mime2java; static { // we need to load the mapping tables used by javaCharset() and mimeCharset(). loadCharacterSetMappings(); } public static InputStream decode(InputStream in, String encoding) throws MessagingException { encoding = encoding.toLowerCase(); // some encodies are just pass-throughs, with no real decoding. if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { return in; } else if (encoding.equals("base64")) { return new Base64DecoderStream(in); } // UUEncode is known by a couple historical extension names too. else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { return new UUDecoderStream(in); } else if (encoding.equals("quoted-printable")) { return new QuotedPrintableDecoderStream(in); } else { throw new MessagingException("Unknown encoding " + encoding); } } /** * Decode a string of text obtained from a mail header into * it's proper form. The text generally will consist of a * string of tokens, some of which may be encoded using * base64 encoding. * * @param text The text to decode. * * @return The decoded test string. * @exception UnsupportedEncodingException */ public static String decodeText(String text) throws UnsupportedEncodingException { // if the text contains any encoded tokens, those tokens will be marked with "=?". If the // source string doesn't contain that sequent, no decoding is required. if (text.indexOf("=?") < 0) { return text; } // we have two sets of rules we can apply. if (!SessionUtil.getBooleanProperty(MIME_DECODE_TEXT_STRICT, true)) { return decodeTextNonStrict(text); } int offset = 0; int endOffset = text.length(); int startWhiteSpace = -1; int endWhiteSpace = -1; StringBuffer decodedText = new StringBuffer(text.length()); boolean previousTokenEncoded = false; while (offset < endOffset) { char ch = text.charAt(offset); // is this a whitespace character? if (linearWhiteSpace.indexOf(ch) != -1) { startWhiteSpace = offset; while (offset < endOffset) { // step over the white space characters. ch = text.charAt(offset); if (linearWhiteSpace.indexOf(ch) != -1) { offset++; } else { // record the location of the first non lwsp and drop down to process the // token characters. endWhiteSpace = offset; break; } } } else { // we have a word token. We need to scan over the word and then try to parse it. int wordStart = offset; while (offset < endOffset) { // step over the white space characters. ch = text.charAt(offset); if (linearWhiteSpace.indexOf(ch) == -1) { offset++; } else { break; } //NB: Trailing whitespace on these header strings will just be discarded. } // pull out the word token. String word = text.substring(wordStart, offset); // is the token encoded? decode the word if (word.startsWith("=?")) { try { // if this gives a parsing failure, treat it like a non-encoded word. String decodedWord = decodeWord(word); // are any whitespace characters significant? Append 'em if we've got 'em. if (!previousTokenEncoded) { if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } } // this is definitely a decoded token. previousTokenEncoded = true; // and add this to the text. decodedText.append(decodedWord); // we continue parsing from here...we allow parsing errors to fall through // and get handled as normal text. continue; } catch (ParseException e) { } } // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(word); } } return decodedText.toString(); } /** * Decode a string of text obtained from a mail header into * it's proper form. The text generally will consist of a * string of tokens, some of which may be encoded using * base64 encoding. This is for non-strict decoded for mailers that * violate the RFC 2047 restriction that decoded tokens must be delimited * by linear white space. This will scan tokens looking for inner tokens * enclosed in "=?" -- "?=" pairs. * * @param text The text to decode. * * @return The decoded test string. * @exception UnsupportedEncodingException */ private static String decodeTextNonStrict(String text) throws UnsupportedEncodingException { int offset = 0; int endOffset = text.length(); int startWhiteSpace = -1; int endWhiteSpace = -1; StringBuffer decodedText = new StringBuffer(text.length()); boolean previousTokenEncoded = false; while (offset < endOffset) { char ch = text.charAt(offset); // is this a whitespace character? if (linearWhiteSpace.indexOf(ch) != -1) { startWhiteSpace = offset; while (offset < endOffset) { // step over the white space characters. ch = text.charAt(offset); if (linearWhiteSpace.indexOf(ch) != -1) { offset++; } else { // record the location of the first non lwsp and drop down to process the // token characters. endWhiteSpace = offset; break; } } } else { // we're at the start of a word token. We potentially need to break this up into subtokens int wordStart = offset; while (offset < endOffset) { // step over the white space characters. ch = text.charAt(offset); if (linearWhiteSpace.indexOf(ch) == -1) { offset++; } else { break; } //NB: Trailing whitespace on these header strings will just be discarded. } // pull out the word token. String word = text.substring(wordStart, offset); int decodeStart = 0; // now scan and process each of the bits within here. while (decodeStart < word.length()) { int tokenStart = word.indexOf("=?", decodeStart); if (tokenStart == -1) { // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(word.substring(decodeStart)); // we're finished. break; } // we have something to process else { // we might have a normal token preceeding this. if (tokenStart != decodeStart) { // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(word.substring(decodeStart, tokenStart)); } // now find the end marker. int tokenEnd = word.indexOf("?=", tokenStart); // sigh, an invalid token. Treat this as plain text. if (tokenEnd == -1) { // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(word.substring(tokenStart)); // we're finished. break; } else { // update our ticker decodeStart = tokenEnd + 2; String token = word.substring(tokenStart, tokenEnd); try { // if this gives a parsing failure, treat it like a non-encoded word. String decodedWord = decodeWord(token); // are any whitespace characters significant? Append 'em if we've got 'em. if (!previousTokenEncoded) { if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } } // this is definitely a decoded token. previousTokenEncoded = true; // and add this to the text. decodedText.append(decodedWord); // we continue parsing from here...we allow parsing errors to fall through // and get handled as normal text. continue; } catch (ParseException e) { } // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(token); } } } } } return decodedText.toString(); } /** * Parse a string using the RFC 2047 rules for an "encoded-word" * type. This encoding has the syntax: * * encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" * * @param word The possibly encoded word value. * * @return The decoded word. * @exception ParseException * @exception UnsupportedEncodingException */ public static String decodeWord(String word) throws ParseException, UnsupportedEncodingException { // encoded words start with the characters "=?". If this not an encoded word, we throw a // ParseException for the caller. if (!word.startsWith("=?")) { throw new ParseException("Invalid RFC 2047 encoded-word: " + word); } int charsetPos = word.indexOf('?', 2); if (charsetPos == -1) { throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word); } // pull out the character set information (this is the MIME name at this point). String charset = word.substring(2, charsetPos).toLowerCase(); // now pull out the encoding token the same way. int encodingPos = word.indexOf('?', charsetPos + 1); if (encodingPos == -1) { throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word); } String encoding = word.substring(charsetPos + 1, encodingPos); // and finally the encoded text. int encodedTextPos = word.indexOf("?=", encodingPos + 1); if (encodedTextPos == -1) { throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word); } String encodedText = word.substring(encodingPos + 1, encodedTextPos); // seems a bit silly to encode a null string, but easy to deal with. if (encodedText.length() == 0) { return ""; } try { // the decoder writes directly to an output stream. ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length()); byte[] encodedData = encodedText.getBytes("US-ASCII"); // Base64 encoded? if (encoding.equals("B")) { Base64.decode(encodedData, out); } // maybe quoted printable. else if (encoding.equals("Q")) { QuotedPrintableEncoder dataEncoder = new QuotedPrintableEncoder(); dataEncoder.decodeWord(encodedData, out); } else { throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding); } // get the decoded byte data and convert into a string. byte[] decodedData = out.toByteArray(); return new String(decodedData, javaCharset(charset)); } catch (IOException e) { throw new UnsupportedEncodingException("Invalid RFC 2047 encoding"); } } /** * Wrap an encoder around a given output stream. * * @param out The output stream to wrap. * @param encoding The name of the encoding. * * @return A instance of FilterOutputStream that manages on the fly * encoding for the requested encoding type. * @exception MessagingException */ public static OutputStream encode(OutputStream out, String encoding) throws MessagingException { // no encoding specified, so assume it goes out unchanged. if (encoding == null) { return out; } encoding = encoding.toLowerCase(); // some encodies are just pass-throughs, with no real decoding. if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { return out; } else if (encoding.equals("base64")) { return new Base64EncoderStream(out); } // UUEncode is known by a couple historical extension names too. else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { return new UUEncoderStream(out); } else if (encoding.equals("quoted-printable")) { return new QuotedPrintableEncoderStream(out); } else { throw new MessagingException("Unknown encoding " + encoding); } } /** * Wrap an encoder around a given output stream. * * @param out The output stream to wrap. * @param encoding The name of the encoding. * @param filename The filename of the data being sent (only used for UUEncode). * * @return A instance of FilterOutputStream that manages on the fly * encoding for the requested encoding type. * @exception MessagingException */ public static OutputStream encode(OutputStream out, String encoding, String filename) throws MessagingException { encoding = encoding.toLowerCase(); // some encodies are just pass-throughs, with no real decoding. if (encoding.equals("binary") || encoding.equals("7bit") || encoding.equals("8bit")) { return out; } else if (encoding.equals("base64")) { return new Base64EncoderStream(out); } // UUEncode is known by a couple historical extension names too. else if (encoding.equals("uuencode") || encoding.equals("x-uuencode") || encoding.equals("x-uue")) { return new UUEncoderStream(out, filename); } else if (encoding.equals("quoted-printable")) { return new QuotedPrintableEncoderStream(out); } else { throw new MessagingException("Unknown encoding " + encoding); } } public static String encodeText(String word) throws UnsupportedEncodingException { return encodeText(word, null, null); } public static String encodeText(String word, String charset, String encoding) throws UnsupportedEncodingException { return encodeWord(word, charset, encoding, false); } public static String encodeWord(String word) throws UnsupportedEncodingException { return encodeWord(word, null, null); } public static String encodeWord(String word, String charset, String encoding) throws UnsupportedEncodingException { return encodeWord(word, charset, encoding, true); } private static String encodeWord(String word, String charset, String encoding, boolean encodingWord) throws UnsupportedEncodingException { // figure out what we need to encode this. String encoder = ASCIIUtil.getTextTransferEncoding(word); // all ascii? We can return this directly, if (encoder.equals("7bit")) { return word; } // if not given a charset, use the default. if (charset == null) { charset = getDefaultMIMECharset(); } // sort out the encoder. If not explicitly given, use the best guess we've already established. if (encoding != null) { if (encoding.equalsIgnoreCase("B")) { encoder = "base64"; } else if (encoding.equalsIgnoreCase("Q")) { encoder = "quoted-printable"; } else { throw new UnsupportedEncodingException("Unknown transfer encoding: " + encoding); } } try { // we'll format this directly into the string buffer StringBuffer result = new StringBuffer(); // this is the maximum size of a segment of encoded data, which is based off // of a 75 character size limit and all of the encoding overhead elements. int sizeLimit = 75 - 7 - charset.length(); // now do the appropriate encoding work if (encoder.equals("base64")) { Base64Encoder dataEncoder = new Base64Encoder(); // this may recurse on the encoding if the string is too long. The left-most will not // get a segment delimiter encodeBase64(word, result, sizeLimit, charset, dataEncoder, true, SessionUtil.getBooleanProperty(MIME_FOLDENCODEDWORDS, false)); } else { QuotedPrintableEncoder dataEncoder = new QuotedPrintableEncoder(); encodeQuotedPrintable(word, result, sizeLimit, charset, dataEncoder, true, SessionUtil.getBooleanProperty(MIME_FOLDENCODEDWORDS, false), encodingWord ? QP_WORD_SPECIALS : QP_TEXT_SPECIALS); } return result.toString(); } catch (IOException e) { throw new UnsupportedEncodingException("Invalid encoding"); } } /** * Encode a string into base64 encoding, taking into * account the maximum segment length. * * @param data The string data to encode. * @param out The output buffer used for the result. * @param sizeLimit The maximum amount of encoded data we're allowed * to have in a single encoded segment. * @param charset The character set marker that needs to be added to the * encoding header. * @param encoder The encoder instance we're using. * @param firstSegment * If true, this is the first (left-most) segment in the * data. Used to determine if segment delimiters need to * be added between sections. * @param foldSegments * Indicates the type of delimiter to use (blank or newline sequence). */ static private void encodeBase64(String data, StringBuffer out, int sizeLimit, String charset, Base64Encoder encoder, boolean firstSegment, boolean foldSegments) throws IOException { // this needs to be converted into the appropriate transfer encoding. byte [] bytes = data.getBytes(javaCharset(charset)); int estimatedSize = encoder.estimateEncodedLength(bytes); // if the estimated encoding size is over our segment limit, split the string in half and // recurse. Eventually we'll reach a point where things are small enough. if (estimatedSize > sizeLimit) { // the first segment indicator travels with the left half. encodeBase64(data.substring(0, data.length() / 2), out, sizeLimit, charset, encoder, firstSegment, foldSegments); // the second half can never be the first segment encodeBase64(data.substring(data.length() / 2), out, sizeLimit, charset, encoder, false, foldSegments); } else { // if this is not the first sement of the encoding, we need to add either a blank or // a newline sequence to the data if (!firstSegment) { if (foldSegments) { out.append("\r\n"); } else { out.append(' '); } } // do the encoding of the segment. encoder.encodeWord(bytes, out, charset); } } /** * Encode a string into quoted printable encoding, taking into * account the maximum segment length. * * @param data The string data to encode. * @param out The output buffer used for the result. * @param sizeLimit The maximum amount of encoded data we're allowed * to have in a single encoded segment. * @param charset The character set marker that needs to be added to the * encoding header. * @param encoder The encoder instance we're using. * @param firstSegment * If true, this is the first (left-most) segment in the * data. Used to determine if segment delimiters need to * be added between sections. * @param foldSegments * Indicates the type of delimiter to use (blank or newline sequence). */ static private void encodeQuotedPrintable(String data, StringBuffer out, int sizeLimit, String charset, QuotedPrintableEncoder encoder, boolean firstSegment, boolean foldSegments, String specials) throws IOException { // this needs to be converted into the appropriate transfer encoding. byte [] bytes = data.getBytes(javaCharset(charset)); int estimatedSize = encoder.estimateEncodedLength(bytes, specials); // if the estimated encoding size is over our segment limit, split the string in half and // recurse. Eventually we'll reach a point where things are small enough. if (estimatedSize > sizeLimit) { // the first segment indicator travels with the left half. encodeQuotedPrintable(data.substring(0, data.length() / 2), out, sizeLimit, charset, encoder, firstSegment, foldSegments, specials); // the second half can never be the first segment encodeQuotedPrintable(data.substring(data.length() / 2), out, sizeLimit, charset, encoder, false, foldSegments, specials); } else { // if this is not the first sement of the encoding, we need to add either a blank or // a newline sequence to the data if (!firstSegment) { if (foldSegments) { out.append("\r\n"); } else { out.append(' '); } } // do the encoding of the segment. encoder.encodeWord(bytes, out, charset, specials); } } /** * Examine the content of a data source and decide what type * of transfer encoding should be used. For text streams, * we'll decided between 7bit, quoted-printable, and base64. * For binary content types, we'll use either 7bit or base64. * * @param handler The DataHandler associated with the content. * * @return The string name of an encoding used to transfer the content. */ public static String getEncoding(DataHandler handler) { // if this handler has an associated data source, we can read directly from the // data source to make this judgment. This is generally MUCH faster than asking the // DataHandler to write out the data for us. DataSource ds = handler.getDataSource(); if (ds != null) { return getEncoding(ds); } try { // get a parser that allows us to make comparisons. ContentType content = new ContentType(ds.getContentType()); // The only access to the content bytes at this point is by asking the handler to write // the information out to a stream. We're going to pipe this through a special stream // that examines the bytes as they go by. ContentCheckingOutputStream checker = new ContentCheckingOutputStream(); handler.writeTo(checker); // figure this out based on whether we believe this to be a text type or not. if (content.match("text/*")) { return checker.getTextTransferEncoding(); } else { return checker.getBinaryTransferEncoding(); } } catch (Exception e) { // any unexpected I/O exceptions we'll force to a "safe" fallback position. return "base64"; } } /** * Determine the what transfer encoding should be used for * data retrieved from a DataSource. * * @param source The DataSource for the transmitted data. * * @return The string name of the encoding form that should be used for * the data. */ public static String getEncoding(DataSource source) { InputStream in = null; try { // get a parser that allows us to make comparisons. ContentType content = new ContentType(source.getContentType()); // we're probably going to have to scan the data. in = source.getInputStream(); if (!content.match("text/*")) { // Not purporting to be a text type? Examine the content to see we might be able to // at least pretend it is an ascii type. return ASCIIUtil.getBinaryTransferEncoding(in); } else { return ASCIIUtil.getTextTransferEncoding(in); } } catch (Exception e) { // this was a problem...not sure what makes sense here, so we'll assume it's binary // and we need to transfer this using Base64 encoding. return "base64"; } finally { // make sure we close the stream try { if (in != null) { in.close(); } } catch (IOException e) { } } } /** * Quote a "word" value. If the word contains any character from * the specified "specials" list, this value is returned as a * quoted strong. Otherwise, it is returned unchanged (an "atom"). * * @param word The word requiring quoting. * @param specials The set of special characters that can't appear in an unquoted * string. * * @return The quoted value. This will be unchanged if the word doesn't contain * any of the designated special characters. */ public static String quote(String word, String specials) { int wordLength = word.length(); boolean requiresQuoting = false; // scan the string looking for problem characters for (int i =0; i < wordLength; i++) { char ch = word.charAt(i); // special escaped characters require escaping, which also implies quoting. if (escapedChars.indexOf(ch) >= 0) { return quoteAndEscapeString(word); } // now check for control characters or the designated special characters. if (ch < 32 || ch >= 127 || specials.indexOf(ch) >= 0) { // we know this requires quoting, but we still need to scan the entire string to // see if contains chars that require escaping. Just go ahead and treat it as if it does. return quoteAndEscapeString(word); } } return word; } /** * Take a string and return it as a formatted quoted string, with * all characters requiring escaping handled properly. * * @param word The string to quote. * * @return The quoted string. */ private static String quoteAndEscapeString(String word) { int wordLength = word.length(); // allocate at least enough for the string and two quotes plus a reasonable number of escaped chars. StringBuffer buffer = new StringBuffer(wordLength + 10); // add the leading quote. buffer.append('"'); for (int i = 0; i < wordLength; i++) { char ch = word.charAt(i); // is this an escaped char? if (escapedChars.indexOf(ch) >= 0) { // add the escape marker before appending. buffer.append('\\'); } buffer.append(ch); } // now the closing quote buffer.append('"'); return buffer.toString(); } /** * Translate a MIME standard character set name into the Java * equivalent. * * @param charset The MIME standard name. * * @return The Java equivalent for this name. */ public static String javaCharset(String charset) { // nothing in, nothing out. if (charset == null) { return null; } String mappedCharset = (String)mime2java.get(charset.toLowerCase()); // if there is no mapping, then the original name is used. Many of the MIME character set // names map directly back into Java. The reverse isn't necessarily true. return mappedCharset == null ? charset : mappedCharset; } /** * Map a Java character set name into the MIME equivalent. * * @param charset The java character set name. * * @return The MIME standard equivalent for this character set name. */ public static String mimeCharset(String charset) { // nothing in, nothing out. if (charset == null) { return null; } String mappedCharset = (String)java2mime.get(charset.toLowerCase()); // if there is no mapping, then the original name is used. Many of the MIME character set // names map directly back into Java. The reverse isn't necessarily true. return mappedCharset == null ? charset : mappedCharset; } /** * Get the default character set to use, in Java name format. * This either be the value set with the mail.mime.charset * system property or obtained from the file.encoding system * property. If neither of these is set, we fall back to * 8859_1 (basically US-ASCII). * * @return The character string value of the default character set. */ public static String getDefaultJavaCharset() { String charset = SessionUtil.getProperty("mail.mime.charset"); if (charset != null) { return javaCharset(charset); } return SessionUtil.getProperty("file.encoding", "8859_1"); } /** * Get the default character set to use, in MIME name format. * This either be the value set with the mail.mime.charset * system property or obtained from the file.encoding system * property. If neither of these is set, we fall back to * 8859_1 (basically US-ASCII). * * @return The character string value of the default character set. */ static String getDefaultMIMECharset() { // if the property is specified, this can be used directly. String charset = SessionUtil.getProperty("mail.mime.charset"); if (charset != null) { return charset; } // get the Java-defined default and map back to a MIME name. return mimeCharset(SessionUtil.getProperty("file.encoding", "8859_1")); } /** * Load the default mapping tables used by the javaCharset() * and mimeCharset() methods. By default, these tables are * loaded from the /META-INF/javamail.charset.map file. If * something goes wrong loading that file, we configure things * with a default mapping table (which just happens to mimic * what's in the default mapping file). */ static private void loadCharacterSetMappings() { java2mime = new HashMap(); mime2java = new HashMap(); // normally, these come from a character map file contained in the jar file. try { InputStream map = javax.mail.internet.MimeUtility.class.getResourceAsStream("/META-INF/javamail.charset.map"); if (map != null) { // get a reader for this so we can load. BufferedReader reader = new BufferedReader(new InputStreamReader(map)); readMappings(reader, java2mime); readMappings(reader, mime2java); } } catch (Exception e) { } // if any sort of error occurred reading the preferred file version, we could end up with empty // mapping tables. This could cause all sorts of difficulty, so ensure they are populated with at // least a reasonable set of defaults. // these mappings echo what's in the default file. if (java2mime.isEmpty()) { java2mime.put("8859_1", "ISO-8859-1"); java2mime.put("iso8859_1", "ISO-8859-1"); java2mime.put("iso8859-1", "ISO-8859-1"); java2mime.put("8859_2", "ISO-8859-2"); java2mime.put("iso8859_2", "ISO-8859-2"); java2mime.put("iso8859-2", "ISO-8859-2"); java2mime.put("8859_3", "ISO-8859-3"); java2mime.put("iso8859_3", "ISO-8859-3"); java2mime.put("iso8859-3", "ISO-8859-3"); java2mime.put("8859_4", "ISO-8859-4"); java2mime.put("iso8859_4", "ISO-8859-4"); java2mime.put("iso8859-4", "ISO-8859-4"); java2mime.put("8859_5", "ISO-8859-5"); java2mime.put("iso8859_5", "ISO-8859-5"); java2mime.put("iso8859-5", "ISO-8859-5"); java2mime.put ("8859_6", "ISO-8859-6"); java2mime.put("iso8859_6", "ISO-8859-6"); java2mime.put("iso8859-6", "ISO-8859-6"); java2mime.put("8859_7", "ISO-8859-7"); java2mime.put("iso8859_7", "ISO-8859-7"); java2mime.put("iso8859-7", "ISO-8859-7"); java2mime.put("8859_8", "ISO-8859-8"); java2mime.put("iso8859_8", "ISO-8859-8"); java2mime.put("iso8859-8", "ISO-8859-8"); java2mime.put("8859_9", "ISO-8859-9"); java2mime.put("iso8859_9", "ISO-8859-9"); java2mime.put("iso8859-9", "ISO-8859-9"); java2mime.put("sjis", "Shift_JIS"); java2mime.put ("jis", "ISO-2022-JP"); java2mime.put("iso2022jp", "ISO-2022-JP"); java2mime.put("euc_jp", "euc-jp"); java2mime.put("koi8_r", "koi8-r"); java2mime.put("euc_cn", "euc-cn"); java2mime.put("euc_tw", "euc-tw"); java2mime.put("euc_kr", "euc-kr"); } if (mime2java.isEmpty ()) { mime2java.put("iso-2022-cn", "ISO2022CN"); mime2java.put("iso-2022-kr", "ISO2022KR"); mime2java.put("utf-8", "UTF8"); mime2java.put("utf8", "UTF8"); mime2java.put("ja_jp.iso2022-7", "ISO2022JP"); mime2java.put("ja_jp.eucjp", "EUCJIS"); mime2java.put ("euc-kr", "KSC5601"); mime2java.put("euckr", "KSC5601"); mime2java.put("us-ascii", "ISO-8859-1"); mime2java.put("x-us-ascii", "ISO-8859-1"); } } /** * Read a section of a character map table and populate the * target mapping table with the information. The table end * is marked by a line starting with "--" and also ending with * "--". Blank lines and comment lines (beginning with '#') are * ignored. * * @param reader The source of the file information. * @param table The mapping table used to store the information. */ static private void readMappings(BufferedReader reader, Map table) throws IOException { // process lines to the EOF or the end of table marker. while (true) { String line = reader.readLine(); // no line returned is an EOF if (line == null) { return; } // trim so we're not messed up by trailing blanks line = line.trim(); if (line.length() == 0 || line.startsWith("#")) { continue; } // stop processing if this is the end-of-table marker. if (line.startsWith("--") && line.endsWith("--")) { return; } // we allow either blanks or tabs as token delimiters. StringTokenizer tokenizer = new StringTokenizer(line, " \t"); try { String from = tokenizer.nextToken().toLowerCase(); String to = tokenizer.nextToken(); table.put(from, to); } catch (NoSuchElementException e) { // just ignore the line if invalid. } } } /** * Perform RFC 2047 text folding on a string of text. * * @param used The amount of text already "used up" on this line. This is * typically the length of a message header that this text * get getting added to. * @param s The text to fold. * * @return The input text, with linebreaks inserted at appropriate fold points. */ public static String fold(int used, String s) { // if folding is disable, unfolding is also. Return the string unchanged. if (!SessionUtil.getBooleanProperty(MIME_FOLDTEXT, true)) { return s; } int end; // now we need to strip off any trailing "whitespace", where whitespace is blanks, tabs, // and line break characters. for (end = s.length() - 1; end >= 0; end--) { int ch = s.charAt(end); if (ch != ' ' && ch != '\t' ) { break; } } // did we actually find something to remove? Shorten the String to the trimmed length if (end != s.length() - 1) { s = s.substring(0, end + 1); } // does the string as it exists now not require folding? We can just had that back right off. if (s.length() + used <= FOLD_THRESHOLD) { return s; } // get a buffer for the length of the string, plus room for a few line breaks. // these are soft line breaks, so we generally need more that just the line breaks (an escape + // CR + LF + leading space on next line); StringBuffer newString = new StringBuffer(s.length() + 8); // now keep chopping this down until we've accomplished what we need. while (used + s.length() > FOLD_THRESHOLD) { int breakPoint = -1; char breakChar = 0; // now scan for the next place where we can break. for (int i = 0; i < s.length(); i++) { // have we passed the fold limit? if (used + i > FOLD_THRESHOLD) { // if we've already seen a blank, then stop now. Otherwise // we keep going until we hit a fold point. if (breakPoint != -1) { break; } } char ch = s.charAt(i); // a white space character? if (ch == ' ' || ch == '\t') { // this might be a run of white space, so skip over those now. breakPoint = i; // we need to maintain the same character type after the inserted linebreak. breakChar = ch; i++; while (i < s.length()) { ch = s.charAt(i); if (ch != ' ' && ch != '\t') { break; } i++; } } // found an embedded new line. Escape this so that the unfolding process preserves it. else if (ch == '\n') { newString.append('\\'); newString.append('\n'); } else if (ch == '\r') { newString.append('\\'); newString.append('\n'); i++; // if this is a CRLF pair, add the second char also if (i < s.length() && s.charAt(i) == '\n') { newString.append('\r'); } } } // no fold point found, we punt, append the remainder and leave. if (breakPoint == -1) { newString.append(s); return newString.toString(); } newString.append(s.substring(0, breakPoint)); newString.append("\r\n"); newString.append(breakChar); // chop the string s = s.substring(breakPoint + 1); // start again, and we've used the first char of the limit already with the whitespace char. used = 1; } // add on the remainder, and return newString.append(s); return newString.toString(); } /** * Unfold a folded string. The unfolding process will remove * any line breaks that are not escaped and which are also followed * by whitespace characters. * * @param s The folded string. * * @return A new string with unfolding rules applied. */ public static String unfold(String s) { // if folding is disable, unfolding is also. Return the string unchanged. if (!SessionUtil.getBooleanProperty(MIME_FOLDTEXT, true)) { return s; } // if there are no line break characters in the string, we can just return this. if (s.indexOf('\n') < 0 && s.indexOf('\r') < 0) { return s; } // we need to scan and fix things up. int length = s.length(); StringBuffer newString = new StringBuffer(length); // scan the entire string for (int i = 0; i < length; i++) { char ch = s.charAt(i); // we have a backslash. In folded strings, escape characters are only processed as such if // they preceed line breaks. Otherwise, we leave it be. if (ch == '\\') { // escape at the very end? Just add the character. if (i == length - 1) { newString.append(ch); } else { int nextChar = s.charAt(i + 1); // naked newline? Add the new line to the buffer, and skip the escape char. if (nextChar == '\n') { newString.append('\n'); i++; } else if (nextChar == '\r') { // just the CR left? Add it, removing the escape. if (i == length - 2 || s.charAt(i + 2) != '\r') { newString.append('\r'); i++; } else { // toss the escape, add both parts of the CRLF, and skip over two chars. newString.append('\r'); newString.append('\n'); i += 2; } } else { // an escape for another purpose, just copy it over. newString.append(ch); } } } // we have an unescaped line break else if (ch == '\n' || ch == '\r') { // remember the position in case we need to backtrack. int lineBreak = i; boolean CRLF = false; if (ch == '\r') { // check to see if we need to step over this. if (i < length - 1 && s.charAt(i + 1) == '\n') { i++; // flag the type so we know what we might need to preserve. CRLF = true; } } // get a temp position scanner. int scan = i + 1; // does a blank follow this new line? we need to scrap the new line and reduce the leading blanks // down to a single blank. if (scan < length && s.charAt(scan) == ' ') { // add the character newString.append(' '); // scan over the rest of the blanks i = scan + 1; while (i < length && s.charAt(i) == ' ') { i++; } // we'll increment down below, so back up to the last blank as the current char. i--; } else { // we must keep this line break. Append the appropriate style. if (CRLF) { newString.append("\r\n"); } else { newString.append(ch); } } } else { // just a normal, ordinary character newString.append(ch); } } return newString.toString(); } } /** * Utility class for examining content information written out * by a DataHandler object. This stream gathers statistics on * the stream so it can make transfer encoding determinations. */ class ContentCheckingOutputStream extends OutputStream { private int asciiChars = 0; private int nonAsciiChars = 0; private boolean containsLongLines = false; private boolean containsMalformedEOL = false; private int previousChar = 0; private int span = 0; ContentCheckingOutputStream() { } public void write(byte[] data) throws IOException { write(data, 0, data.length); } public void write(byte[] data, int offset, int length) throws IOException { for (int i = 0; i < length; i++) { write(data[offset + i]); } } public void write(int ch) { // we found a linebreak. Reset the line length counters on either one. We don't // really need to validate here. if (ch == '\n' || ch == '\r') { // we found a newline, this is only valid if the previous char was the '\r' if (ch == '\n') { // malformed linebreak? force this to base64 encoding. if (previousChar != '\r') { containsMalformedEOL = true; } } // hit a line end, reset our line length counter span = 0; } else { span++; // the text has long lines, we can't transfer this as unencoded text. if (span > 998) { containsLongLines = true; } // non-ascii character, we have to transfer this in binary. if (!ASCIIUtil.isAscii(ch)) { nonAsciiChars++; } else { asciiChars++; } } previousChar = ch; } public String getBinaryTransferEncoding() { if (nonAsciiChars != 0 || containsLongLines || containsMalformedEOL) { return "base64"; } else { return "7bit"; } } public String getTextTransferEncoding() { // looking good so far, only valid chars here. if (nonAsciiChars == 0) { // does this contain long text lines? We need to use a Q-P encoding which will // be only slightly longer, but handles folding the longer lines. if (containsLongLines) { return "quoted-printable"; } else { // ideal! Easiest one to handle. return "7bit"; } } else { // mostly characters requiring encoding? Base64 is our best bet. if (nonAsciiChars > asciiChars) { return "base64"; } else { // Q-P encoding will use fewer bytes than the full Base64. return "quoted-printable"; } } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/InternetAddress.java0000664000175000017500000005243611345370365027624 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import javax.mail.Address; import javax.mail.Session; import org.apache.geronimo.mail.util.SessionUtil; /** * A representation of an Internet email address as specified by RFC822 in * conjunction with a human-readable personal name that can be encoded as * specified by RFC2047. * A typical address is "user@host.domain" and personal name "Joe User" * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class InternetAddress extends Address implements Cloneable { private static final long serialVersionUID = -7507595530758302903L; /** * The address in RFC822 format. */ protected String address; /** * The personal name in RFC2047 format. * Subclasses must ensure that this field is updated if the personal field * is updated; alternatively, it can be invalidated by setting to null * which will cause it to be recomputed. */ protected String encodedPersonal; /** * The personal name as a Java String. * Subclasses must ensure that this field is updated if the encodedPersonal field * is updated; alternatively, it can be invalidated by setting to null * which will cause it to be recomputed. */ protected String personal; public InternetAddress() { } public InternetAddress(String address) throws AddressException { this(address, true); } public InternetAddress(String address, boolean strict) throws AddressException { // use the parse method to process the address. This has the wierd side effect of creating a new // InternetAddress instance to create an InternetAddress, but these are lightweight objects and // we need access to multiple pieces of data from the parsing process. AddressParser parser = new AddressParser(address, strict ? AddressParser.STRICT : AddressParser.NONSTRICT); InternetAddress parsedAddress = parser.parseAddress(); // copy the important information, which right now is just the address and // personal info. this.address = parsedAddress.address; this.personal = parsedAddress.personal; this.encodedPersonal = parsedAddress.encodedPersonal; } public InternetAddress(String address, String personal) throws UnsupportedEncodingException { this(address, personal, null); } public InternetAddress(String address, String personal, String charset) throws UnsupportedEncodingException { this.address = address; setPersonal(personal, charset); } /** * Clone this object. * * @return a copy of this object as created by Object.clone() */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new Error(); } } /** * Return the type of this address. * * @return the type of this address; always "rfc822" */ public String getType() { return "rfc822"; } /** * Set the address. * No validation is performed; validate() can be used to check if it is valid. * * @param address the address to set */ public void setAddress(String address) { this.address = address; } /** * Set the personal name. * The name is first checked to see if it can be encoded; if this fails then an * UnsupportedEncodingException is thrown and no fields are modified. * * @param name the new personal name * @param charset the charset to use; see {@link MimeUtility#encodeWord(String, String, String) MimeUtilityencodeWord} * @throws UnsupportedEncodingException if the name cannot be encoded */ public void setPersonal(String name, String charset) throws UnsupportedEncodingException { personal = name; if (name != null) { encodedPersonal = MimeUtility.encodeWord(name, charset, null); } else { encodedPersonal = null; } } /** * Set the personal name. * The name is first checked to see if it can be encoded using {@link MimeUtility#encodeWord(String)}; if this fails then an * UnsupportedEncodingException is thrown and no fields are modified. * * @param name the new personal name * @throws UnsupportedEncodingException if the name cannot be encoded */ public void setPersonal(String name) throws UnsupportedEncodingException { personal = name; if (name != null) { encodedPersonal = MimeUtility.encodeWord(name); } else { encodedPersonal = null; } } /** * Return the address. * * @return the address */ public String getAddress() { return address; } /** * Return the personal name. * If the personal field is null, then an attempt is made to decode the encodedPersonal * field using {@link MimeUtility#decodeWord(String)}; if this is sucessful, then * the personal field is updated with that value and returned; if there is a problem * decoding the text then the raw value from encodedPersonal is returned. * * @return the personal name */ public String getPersonal() { if (personal == null && encodedPersonal != null) { try { personal = MimeUtility.decodeWord(encodedPersonal); } catch (ParseException e) { return encodedPersonal; } catch (UnsupportedEncodingException e) { return encodedPersonal; } } return personal; } /** * Return the encoded form of the personal name. * If the encodedPersonal field is null, then an attempt is made to encode the * personal field using {@link MimeUtility#encodeWord(String)}; if this is * successful then the encodedPersonal field is updated with that value and returned; * if there is a problem encoding the text then null is returned. * * @return the encoded form of the personal name */ private String getEncodedPersonal() { if (encodedPersonal == null && personal != null) { try { encodedPersonal = MimeUtility.encodeWord(personal); } catch (UnsupportedEncodingException e) { // as we could not encode this, return null return null; } } return encodedPersonal; } /** * Return a string representation of this address using only US-ASCII characters. * * @return a string representation of this address */ public String toString() { // group addresses are always returned without modification. if (isGroup()) { return address; } // if we have personal information, then we need to return this in the route-addr form: // "personal

". If there is no personal information, then we typically return // the address without the angle brackets. However, if the address contains anything other // than atoms, '@', and '.' (e.g., uses domain literals, has specified routes, or uses // quoted strings in the local-part), we bracket the address. String p = getEncodedPersonal(); if (p == null) { return formatAddress(address); } else { StringBuffer buf = new StringBuffer(p.length() + 8 + address.length() + 3); buf.append(AddressParser.quoteString(p)); buf.append(" <").append(address).append(">"); return buf.toString(); } } /** * Check the form of an address, and enclose it within brackets * if they are required for this address form. * * @param a The source address. * * @return A formatted address, which can be the original address string. */ private String formatAddress(String a) { // this could be a group address....we don't muck with those. if (address.endsWith(";") && address.indexOf(":") > 0) { return address; } if (AddressParser.containsCharacters(a, "()<>,;:\"[]")) { StringBuffer buf = new StringBuffer(address.length() + 3); buf.append("<").append(address).append(">"); return buf.toString(); } return address; } /** * Return a string representation of this address using Unicode characters. * * @return a string representation of this address */ public String toUnicodeString() { // group addresses are always returned without modification. if (isGroup()) { return address; } // if we have personal information, then we need to return this in the route-addr form: // "personal
". If there is no personal information, then we typically return // the address without the angle brackets. However, if the address contains anything other // than atoms, '@', and '.' (e.g., uses domain literals, has specified routes, or uses // quoted strings in the local-part), we bracket the address. // NB: The difference between toString() and toUnicodeString() is the use of getPersonal() // vs. getEncodedPersonal() for the personal portion. If the personal information contains only // ASCII-7 characters, these are the same. String p = getPersonal(); if (p == null) { return formatAddress(address); } else { StringBuffer buf = new StringBuffer(p.length() + 8 + address.length() + 3); buf.append(AddressParser.quoteString(p)); buf.append(" <").append(address).append(">"); return buf.toString(); } } /** * Compares two addresses for equality. * We define this as true if the other object is an InternetAddress * and the two values returned by getAddress() are equal in a * case-insensitive comparison. * * @param o the other object * @return true if the addresses are the same */ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InternetAddress)) return false; InternetAddress other = (InternetAddress) o; String myAddress = getAddress(); return myAddress == null ? (other.getAddress() == null) : myAddress.equalsIgnoreCase(other.getAddress()); } /** * Return the hashCode for this address. * We define this to be the hashCode of the address after conversion to lowercase. * * @return a hashCode for this address */ public int hashCode() { return (address == null) ? 0 : address.toLowerCase().hashCode(); } /** * Return true is this address is an RFC822 group address in the format * phrase ":" [#mailbox] ";". * We check this by using the presense of a ':' character in the address, and a * ';' as the very last character. * * @return true is this address represents a group */ public boolean isGroup() { if (address == null) { return false; } return address.endsWith(";") && address.indexOf(":") > 0; } /** * Return the members of a group address. * * If strict is true and the address does not contain an initial phrase then an AddressException is thrown. * Otherwise the phrase is skipped and the remainder of the address is checked to see if it is a group. * If it is, the content and strict flag are passed to parseHeader to extract the list of addresses; * if it is not a group then null is returned. * * @param strict whether strict RFC822 checking should be performed * @return an array of InternetAddress objects for the group members, or null if this address is not a group * @throws AddressException if there was a problem parsing the header */ public InternetAddress[] getGroup(boolean strict) throws AddressException { if (address == null) { return null; } // create an address parser and use it to extract the group information. AddressParser parser = new AddressParser(address, strict ? AddressParser.STRICT : AddressParser.NONSTRICT); return parser.extractGroupList(); } /** * Return an InternetAddress representing the current user. *

* If session is not null, we first look for an address specified in its * "mail.from" property; if this is not set, we look at its "mail.user" * and "mail.host" properties and if both are not null then an address of * the form "${mail.user}@${mail.host}" is created. * If this fails to give an address, then an attempt is made to create * an address by combining the value of the "user.name" System property * with the value returned from InetAddress.getLocalHost().getHostName(). * Any SecurityException raised accessing the system property or any * UnknownHostException raised getting the hostname are ignored. *

* Finally, an attempt is made to convert the value obtained above to * an InternetAddress. If this fails, then null is returned. * * @param session used to obtain mail properties * @return an InternetAddress for the current user, or null if it cannot be determined */ public static InternetAddress getLocalAddress(Session session) { String host = null; String user = null; // ok, we have several steps for resolving this. To start with, we could have a from address // configured already, which will be a full InternetAddress string. If we don't have that, then // we need to resolve a user and host to compose an address from. if (session != null) { String address = session.getProperty("mail.from"); // if we got this, we can skip out now if (address != null) { try { return new InternetAddress(address); } catch (AddressException e) { // invalid address on the from...treat this as an error and return null. return null; } } // now try for user and host information. We have both session and system properties to check here. // we'll just handle the session ones here, and check the system ones below if we're missing information. user = session.getProperty("mail.user"); host = session.getProperty("mail.host"); } try { // if either user or host is null, then we check non-session sources for the information. if (user == null) { user = System.getProperty("user.name"); } if (host == null) { host = InetAddress.getLocalHost().getHostName(); } if (user != null && host != null) { // if we have both a user and host, we can create a local address return new InternetAddress(user + '@' + host); } } catch (AddressException e) { // ignore } catch (UnknownHostException e) { // ignore } catch (SecurityException e) { // ignore } return null; } /** * Convert the supplied addresses into a single String of comma-separated text as * produced by {@link InternetAddress#toString() toString()}. * No line-break detection is performed. * * @param addresses the array of addresses to convert * @return a one-line String of comma-separated addresses */ public static String toString(Address[] addresses) { if (addresses == null || addresses.length == 0) { return null; } if (addresses.length == 1) { return addresses[0].toString(); } else { StringBuffer buf = new StringBuffer(addresses.length * 32); buf.append(addresses[0].toString()); for (int i = 1; i < addresses.length; i++) { buf.append(", "); buf.append(addresses[i].toString()); } return buf.toString(); } } /** * Convert the supplies addresses into a String of comma-separated text, * inserting line-breaks between addresses as needed to restrict the line * length to 72 characters. Splits will only be introduced between addresses * so an address longer than 71 characters will still be placed on a single * line. * * @param addresses the array of addresses to convert * @param used the starting column * @return a String of comma-separated addresses with optional line breaks */ public static String toString(Address[] addresses, int used) { if (addresses == null || addresses.length == 0) { return null; } if (addresses.length == 1) { String s = addresses[0].toString(); if (used + s.length() > 72) { s = "\r\n " + s; } return s; } else { StringBuffer buf = new StringBuffer(addresses.length * 32); for (int i = 0; i < addresses.length; i++) { String s = addresses[1].toString(); if (i == 0) { if (used + s.length() + 1 > 72) { buf.append("\r\n "); used = 2; } } else { if (used + s.length() + 1 > 72) { buf.append(",\r\n "); used = 2; } else { buf.append(", "); used += 2; } } buf.append(s); used += s.length(); } return buf.toString(); } } /** * Parse addresses out of the string with basic checking. * * @param addresses the addresses to parse * @return an array of InternetAddresses parsed from the string * @throws AddressException if addresses checking fails */ public static InternetAddress[] parse(String addresses) throws AddressException { return parse(addresses, true); } /** * Parse addresses out of the string. * * @param addresses the addresses to parse * @param strict if true perform detailed checking, if false just perform basic checking * @return an array of InternetAddresses parsed from the string * @throws AddressException if address checking fails */ public static InternetAddress[] parse(String addresses, boolean strict) throws AddressException { return parse(addresses, strict ? AddressParser.STRICT : AddressParser.NONSTRICT); } /** * Parse addresses out of the string. * * @param addresses the addresses to parse * @param strict if true perform detailed checking, if false perform little checking * @return an array of InternetAddresses parsed from the string * @throws AddressException if address checking fails */ public static InternetAddress[] parseHeader(String addresses, boolean strict) throws AddressException { return parse(addresses, strict ? AddressParser.STRICT : AddressParser.PARSE_HEADER); } /** * Parse addresses with increasing degrees of RFC822 compliance checking. * * @param addresses the string to parse * @param level The required strictness level. * * @return an array of InternetAddresses parsed from the string * @throws AddressException * if address checking fails */ private static InternetAddress[] parse(String addresses, int level) throws AddressException { // create a parser and have it extract the list using the requested strictness leve. AddressParser parser = new AddressParser(addresses, level); return parser.parseAddressList(); } /** * Validate the address portion of an internet address to ensure * validity. Throws an AddressException if any validity * problems are encountered. * * @exception AddressException */ public void validate() throws AddressException { // create a parser using the strictest validation level. AddressParser parser = new AddressParser(formatAddress(address), AddressParser.STRICT); parser.validateAddress(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/AddressParser.java0000664000175000017500000022130110517560657027262 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; class AddressParser { // the validation strictness levels, from most lenient to most conformant. static public final int NONSTRICT = 0; static public final int PARSE_HEADER = 1; static public final int STRICT = 2; // different mailbox types static protected final int UNKNOWN = 0; static protected final int ROUTE_ADDR = 1; static protected final int GROUP_ADDR = 2; static protected final int SIMPLE_ADDR = 3; // constants for token types. static protected final int END_OF_TOKENS = '\0'; static protected final int PERIOD = '.'; static protected final int LEFT_ANGLE = '<'; static protected final int RIGHT_ANGLE = '>'; static protected final int COMMA = ','; static protected final int AT_SIGN = '@'; static protected final int SEMICOLON = ';'; static protected final int COLON = ':'; static protected final int QUOTED_LITERAL = '"'; static protected final int DOMAIN_LITERAL = '['; static protected final int COMMENT = '('; static protected final int ATOM = 'A'; static protected final int WHITESPACE = ' '; // the string we're parsing private String addresses; // the current parsing position private int position; // the end position of the string private int end; // the strictness flag private int validationLevel; public AddressParser(String addresses, int validation) { this.addresses = addresses; validationLevel = validation; } /** * Parse an address list into an array of internet addresses. * * @return An array containing all of the non-null addresses in the list. * @exception AddressException * Thrown for any validation errors. */ public InternetAddress[] parseAddressList() throws AddressException { // get the address as a set of tokens we can process. TokenStream tokens = tokenizeAddress(); // get an array list accumulator. ArrayList addressList = new ArrayList(); // we process sections of the token stream until we run out of tokens. while (true) { // parse off a single address. Address lists can have null elements, // so this might return a null value. The null value does not get added // to the address accumulator. addressList.addAll(parseSingleAddress(tokens, false)); // This token should be either a "," delimiter or a stream terminator. If we're // at the end, time to get out. AddressToken token = tokens.nextToken(); if (token.type == END_OF_TOKENS) { break; } } return (InternetAddress [])addressList.toArray(new InternetAddress[0]); } /** * Parse a single internet address. This must be a single address, * not an address list. * * @exception AddressException */ public InternetAddress parseAddress() throws AddressException { // get the address as a set of tokens we can process. TokenStream tokens = tokenizeAddress(); // parse off a single address. Address lists can have null elements, // so this might return a null value. The null value does not get added // to the address accumulator. List addressList = parseSingleAddress(tokens, false); // we must get exactly one address back from this. if (addressList.isEmpty()) { throw new AddressException("Null address", addresses, 0); } // this could be a simple list of blank delimited tokens. Ensure we only got one back. if (addressList.size() > 1) { throw new AddressException("Illegal Address", addresses, 0); } // This token must be a stream stream terminator, or we have an error. AddressToken token = tokens.nextToken(); if (token.type != END_OF_TOKENS) { illegalAddress("Illegal Address", token); } return (InternetAddress)addressList.get(0); } /** * Validate an internet address. This must be a single address, * not a list of addresses. The address also must not contain * and personal information to be valid. * * @exception AddressException */ public void validateAddress() throws AddressException { // get the address as a set of tokens we can process. TokenStream tokens = tokenizeAddress(); // parse off a single address. Address lists can have null elements, // so this might return a null value. The null value does not get added // to the address accumulator. List addressList = parseSingleAddress(tokens, false); if (addressList.isEmpty()) { throw new AddressException("Null address", addresses, 0); } // this could be a simple list of blank delimited tokens. Ensure we only got one back. if (addressList.size() > 1) { throw new AddressException("Illegal Address", addresses, 0); } InternetAddress address = (InternetAddress)addressList.get(0); // validation occurs on an address that's already been split into personal and address // data. if (address.personal != null) { throw new AddressException("Illegal Address", addresses, 0); } // This token must be a stream stream terminator, or we have an error. AddressToken token = tokens.nextToken(); if (token.type != END_OF_TOKENS) { illegalAddress("Illegal Address", token); } } /** * Extract the set of address from a group Internet specification. * * @return An array containing all of the non-null addresses in the list. * @exception AddressException */ public InternetAddress[] extractGroupList() throws AddressException { // get the address as a set of tokens we can process. TokenStream tokens = tokenizeAddress(); // get an array list accumulator. ArrayList addresses = new ArrayList(); AddressToken token = tokens.nextToken(); // scan forward to the ':' that starts the group list. If we don't find one, // this is an exception. while (token.type != COLON) { if (token.type == END_OF_TOKENS) { illegalAddress("Missing ':'", token); } token = tokens.nextToken(); } // we process sections of the token stream until we run out of tokens. while (true) { // parse off a single address. Address lists can have null elements, // so this might return a null value. The null value does not get added // to the address accumulator. addresses.addAll(parseSingleAddress(tokens, true)); // This token should be either a "," delimiter or a group terminator. If we're // at the end, this is an error. token = tokens.nextToken(); if (token.type == SEMICOLON) { break; } else if (token.type == END_OF_TOKENS) { illegalAddress("Missing ';'", token); } } return (InternetAddress [])addresses.toArray(new InternetAddress[0]); } /** * Parse out a single address from a string from a string * of address tokens, returning an InternetAddress object that * represents the address. * * @param tokens The token source for this address. * * @return A parsed out and constructed InternetAddress object for * the next address. Returns null if this is an "empty" * address in a list. * @exception AddressException */ private List parseSingleAddress(TokenStream tokens, boolean inGroup) throws AddressException { List parsedAddresses = new ArrayList(); // index markers for personal information AddressToken personalStart = null; AddressToken personalEnd = null; // and similar bits for the address information. AddressToken addressStart = null; AddressToken addressEnd = null; // there is a fall-back set of rules allowed that will parse the address as a set of blank delimited // tokens. However, we do NOT allow this if we encounter any tokens that fall outside of these // rules. For example, comment fields and quoted strings will disallow the very lenient rule set. boolean nonStrictRules = true; // we don't know the type of address yet int addressType = UNKNOWN; // the parsing goes in two stages. Stage one runs through the tokens locating the bounds // of the address we're working on, resolving the personal information, and also validating // some of the larger scale syntax features of an address (matched delimiters for routes and // groups, invalid nesting checks, etc.). // get the next token from the queue and save this. We're going to scan ahead a bit to // figure out what type of address we're looking at, then reset to do the actually parsing // once we've figured out a form. AddressToken first = tokens.nextToken(); // push it back on before starting processing. tokens.pushToken(first); // scan ahead for a trigger token that tells us what we've got. while (addressType == UNKNOWN) { AddressToken token = tokens.nextToken(); switch (token.type) { // skip these for now...after we've processed everything and found that this is a simple // address form, then we'll check for a leading comment token in the first position and use // if as personal information. case COMMENT: // comments do, however, denote that this must be parsed according to RFC822 rules. nonStrictRules = false; break; // a semi-colon when processing a group is an address terminator. we need to // process this like a comma then case SEMICOLON: if (inGroup) { // we need to push the terminator back on for the caller to see. tokens.pushToken(token); // if we've not tagged any tokens as being the address beginning, so this must be a // null address. if (addressStart == null) { // just return the empty list from this. return parsedAddresses; } // the end token is the back part. addressEnd = tokens.previousToken(token); // without a '<' for a route addr, we can't distinguish address tokens from personal data. // We'll use a leading comment, if there is one. personalStart = null; // this is just a simple form. addressType = SIMPLE_ADDR; break; } // NOTE: The above falls through if this is not a group. // any of these tokens are a real token that can be the start of an address. Many of // them are not valid as first tokens in this context, but we flag them later if validation // has been requested. For now, we just mark these as the potential address start. case DOMAIN_LITERAL: case QUOTED_LITERAL: // this set of tokens require fuller RFC822 parsing, so turn off the flag. nonStrictRules = false; case ATOM: case AT_SIGN: case PERIOD: // if we're not determined the start of the address yet, then check to see if we // need to consider this the personal start. if (addressStart == null) { if (personalStart == null) { personalStart = token; } // This is the first real token of the address, which at this point can // be either the personal info or the first token of the address. If we hit // an address terminator without encountering either a route trigger or group // trigger, then this is the real address. addressStart = token; } break; // a LEFT_ANGLE indicates we have a full RFC822 mailbox form. The leading phrase // is the personal info. The address is inside the brackets. case LEFT_ANGLE: // a route address automatically switches off the blank-delimited token mode. nonStrictRules = false; // this is a route address addressType = ROUTE_ADDR; // the address is placed in the InternetAddress object without the route // brackets, so our start is one past this. addressStart = tokens.nextRealToken(); // push this back on the queue so the scanner picks it up properly. tokens.pushToken(addressStart); // make sure we flag the end of the personal section too. if (personalStart != null) { personalEnd = tokens.previousToken(token); } // scan the rest of a route address. addressEnd = scanRouteAddress(tokens, false); break; // a COLON indicates this is a group specifier...parse the group. case COLON: // Colons would not be valid in simple lists, so turn it off. nonStrictRules = false; // if we're scanning a group, we shouldn't encounter a ":". This is a // recursion error if found. if (inGroup) { illegalAddress("Nested group element", token); } addressType = GROUP_ADDR; // groups don't have any personal sections. personalStart = null; // our real start was back at the beginning addressStart = first; addressEnd = scanGroupAddress(tokens); break; // a semi colon can the same as a comma if we're processing a group. // reached the end of string...this might be a null address, or one of the very simple name // forms used for non-strict RFC822 versions. Reset, and try that form case END_OF_TOKENS: // if we're scanning a group, we shouldn't encounter an end token. This is an // error if found. if (inGroup) { illegalAddress("Missing ';'", token); } // NOTE: fall through from above. // this is either a terminator for an address list or a a group terminator. case COMMA: // we need to push the terminator back on for the caller to see. tokens.pushToken(token); // if we've not tagged any tokens as being the address beginning, so this must be a // null address. if (addressStart == null) { // just return the empty list from this. return parsedAddresses; } // the end token is the back part. addressEnd = tokens.previousToken(token); // without a '<' for a route addr, we can't distinguish address tokens from personal data. // We'll use a leading comment, if there is one. personalStart = null; // this is just a simple form. addressType = SIMPLE_ADDR; break; // right angle tokens are pushed, because parsing of the bracketing is not necessarily simple. // we need to flag these here. case RIGHT_ANGLE: illegalAddress("Unexpected '>'", token); } } String personal = null; // if we have personal data, then convert it to a string value. if (personalStart != null) { TokenStream personalTokens = tokens.section(personalStart, personalEnd); personal = personalToString(personalTokens); } // if we have a simple address, then check the first token to see if it's a comment. For simple addresses, // we'll accept the first comment token as the personal information. else { if (addressType == SIMPLE_ADDR && first.type == COMMENT) { personal = first.value; } } TokenStream addressTokens = tokens.section(addressStart, addressEnd); // if this is one of the strictly RFC822 types, then we always validate the address. If this is a // a simple address, then we only validate if strict parsing rules are in effect or we've been asked // to validate. if (validationLevel != PARSE_HEADER) { switch (addressType) { case GROUP_ADDR: validateGroup(addressTokens); break; case ROUTE_ADDR: validateRouteAddr(addressTokens, false); break; case SIMPLE_ADDR: // this is a conditional validation validateSimpleAddress(addressTokens); break; } } // more complex addresses and addresses containing tokens other than just simple addresses // need proper handling. if (validationLevel != NONSTRICT || addressType != SIMPLE_ADDR || !nonStrictRules) { // we might have traversed this already when we validated, so reset the // position before using this again. addressTokens.reset(); String address = addressToString(addressTokens); // get the parsed out sections as string values. InternetAddress result = new InternetAddress(); result.setAddress(address); try { result.setPersonal(personal); } catch (UnsupportedEncodingException e) { } // even though we have a single address, we return this as an array. Simple addresses // can be produce an array of items, so we need to return everything. parsedAddresses.add(result); return parsedAddresses; } else { addressTokens.reset(); TokenStream nextAddress = addressTokens.getBlankDelimitedToken(); while (nextAddress != null) { String address = addressToString(nextAddress); // get the parsed out sections as string values. InternetAddress result = new InternetAddress(); result.setAddress(address); parsedAddresses.add(result); nextAddress = addressTokens.getBlankDelimitedToken(); } return parsedAddresses; } } /** * Scan the token stream, parsing off a route addr spec. This * will do some basic syntax validation, but will not actually * validate any of the address information. Comments will be * discarded. * * @param tokens The stream of tokens. * * @return The last token of the route address (the one preceeding the * terminating '>'. */ private AddressToken scanRouteAddress(TokenStream tokens, boolean inGroup) throws AddressException { // get the first token and ensure we have something between the "<" and ">". AddressToken token = tokens.nextRealToken(); // the last processed non-whitespace token, which is the actual address end once the // right angle bracket is encountered. AddressToken previous = null; // if this route-addr has route information, the first token after the '<' must be a '@'. // this determines if/where a colon or comma can appear. boolean inRoute = token.type == AT_SIGN; // now scan until we reach the terminator. The only validation is done on illegal characters. while (true) { switch (token.type) { // The following tokens are all valid between the brackets, so just skip over them. case ATOM: case QUOTED_LITERAL: case DOMAIN_LITERAL: case PERIOD: case AT_SIGN: break; case COLON: // if not processing route information, this is illegal. if (!inRoute) { illegalAddress("Unexpected ':'", token); } // this is the end of the route information, the rules now change. inRoute = false; break; case COMMA: // if not processing route information, this is illegal. if (!inRoute) { illegalAddress("Unexpected ','", token); } break; case RIGHT_ANGLE: // if previous is null, we've had a route address which is "<>". That's illegal. if (previous == null) { illegalAddress("Illegal address", token); } // step to the next token..this had better be either a comma for another address or // the very end of the address list . token = tokens.nextRealToken(); // if we're scanning part of a group, then the allowed terminators are either ',' or ';'. if (inGroup) { if (token.type != COMMA && token.type != SEMICOLON) { illegalAddress("Illegal address", token); } } // a normal address should have either a ',' for a list or the end. else { if (token.type != COMMA && token.type != END_OF_TOKENS) { illegalAddress("Illegal address", token); } } // we need to push the termination token back on. tokens.pushToken(token); // return the previous token as the updated position. return previous; case END_OF_TOKENS: illegalAddress("Missing '>'", token); // now for the illegal ones in this context. case SEMICOLON: illegalAddress("Unexpected ';'", token); case LEFT_ANGLE: illegalAddress("Unexpected '<'", token); } // remember the previous token. previous = token; token = tokens.nextRealToken(); } } /** * Scan the token stream, parsing off a group address. This * will do some basic syntax validation, but will not actually * validate any of the address information. Comments will be * ignored. * * @param tokens The stream of tokens. * * @return The last token of the group address (the terminating ':"). */ private AddressToken scanGroupAddress(TokenStream tokens) throws AddressException { // A group does not require that there be anything between the ':' and ';". This is // just a group with an empty list. AddressToken token = tokens.nextRealToken(); // now scan until we reach the terminator. The only validation is done on illegal characters. while (true) { switch (token.type) { // The following tokens are all valid in group addresses, so just skip over them. case ATOM: case QUOTED_LITERAL: case DOMAIN_LITERAL: case PERIOD: case AT_SIGN: case COMMA: break; case COLON: illegalAddress("Nested group", token); // route address within a group specifier....we need to at least verify the bracket nesting // and higher level syntax of the route. case LEFT_ANGLE: scanRouteAddress(tokens, true); break; // the only allowed terminator is the ';' case END_OF_TOKENS: illegalAddress("Missing ';'", token); // now for the illegal ones in this context. case SEMICOLON: // verify there's nothing illegal after this. AddressToken next = tokens.nextRealToken(); if (next.type != COMMA && next.type != END_OF_TOKENS) { illegalAddress("Illegal address", token); } // don't forget to put this back on...our caller will need it. tokens.pushToken(next); return token; case RIGHT_ANGLE: illegalAddress("Unexpected '>'", token); } token = tokens.nextRealToken(); } } /** * Parse the provided internet address into a set of tokens. This * phase only does a syntax check on the tokens. The interpretation * of the tokens is the next phase. * * @exception AddressException */ private TokenStream tokenizeAddress() throws AddressException { // get a list for the set of tokens TokenStream tokens = new TokenStream(); end = addresses.length(); // our parsing end marker // now scan along the string looking for the special characters in an internet address. while (moreCharacters()) { char ch = currentChar(); switch (ch) { // start of a comment bit...ignore everything until we hit a closing paren. case '(': scanComment(tokens); break; // a closing paren found outside of normal processing. case ')': syntaxError("Unexpected ')'", position); // start of a quoted string case '"': scanQuotedLiteral(tokens); break; // domain literal case '[': scanDomainLiteral(tokens); break; // a naked closing bracket...not valid except as part of a domain literal. case ']': syntaxError("Unexpected ']'", position); // special character delimiters case '<': tokens.addToken(new AddressToken(LEFT_ANGLE, position)); nextChar(); break; // a naked closing bracket...not valid without a starting one, but // we need to handle this in context. case '>': tokens.addToken(new AddressToken(RIGHT_ANGLE, position)); nextChar(); break; case ':': tokens.addToken(new AddressToken(COLON, position)); nextChar(); break; case ',': tokens.addToken(new AddressToken(COMMA, position)); nextChar(); break; case '.': tokens.addToken(new AddressToken(PERIOD, position)); nextChar(); break; case ';': tokens.addToken(new AddressToken(SEMICOLON, position)); nextChar(); break; case '@': tokens.addToken(new AddressToken(AT_SIGN, position)); nextChar(); break; // white space characters. These are mostly token delimiters, but there are some relaxed // situations where they get processed, so we need to add a white space token for the first // one we encounter in a span. case ' ': case '\t': case '\r': case '\n': // add a single white space token tokens.addToken(new AddressToken(WHITESPACE, position)); nextChar(); // step over any space characters, leaving us positioned either at the end // or the first while (moreCharacters()) { char nextChar = currentChar(); if (nextChar == ' ' || nextChar == '\t' || nextChar == '\r' || nextChar == '\n') { nextChar(); } else { break; } } break; // potentially an atom...if it starts with an allowed atom character, we // parse out the token, otherwise this is invalid. default: if (ch < 040 || ch >= 0177) { syntaxError("Illegal character in address", position); } scanAtom(tokens); break; } } // for this end marker, give an end position. tokens.addToken(new AddressToken(END_OF_TOKENS, addresses.length())); return tokens; } /** * Step to the next character position while parsing. */ private void nextChar() { position++; } /** * Retrieve the character at the current parsing position. * * @return The current character. */ private char currentChar() { return addresses.charAt(position); } /** * Test if there are more characters left to parse. * * @return True if we've hit the last character, false otherwise. */ private boolean moreCharacters() { return position < end; } /** * Parse a quoted string as specified by the RFC822 specification. * * @param tokens The TokenStream where the parsed out token is added. */ private void scanQuotedLiteral(TokenStream tokens) throws AddressException { StringBuffer value = new StringBuffer(); // save the start position for the token. int startPosition = position; // step over the quote delimiter. nextChar(); while (moreCharacters()) { char ch = currentChar(); // is this an escape char? if (ch == '\\') { // step past this, and grab the following character nextChar(); if (!moreCharacters()) { syntaxError("Missing '\"'", position); } value.append(currentChar()); } // end of the string? else if (ch == '"') { // return the constructed string. tokens.addToken(new AddressToken(value.toString(), QUOTED_LITERAL, position)); // step over the close delimiter for the benefit of the next token. nextChar(); return; } // the RFC822 spec disallows CR characters. else if (ch == '\r') { syntaxError("Illegal line end in literal", position); } else { value.append(ch); } nextChar(); } // missing delimiter syntaxError("Missing '\"'", position); } /** * Parse a domain literal as specified by the RFC822 specification. * * @param tokens The TokenStream where the parsed out token is added. */ private void scanDomainLiteral(TokenStream tokens) throws AddressException { StringBuffer value = new StringBuffer(); int startPosition = position; // step over the quote delimiter. nextChar(); while (moreCharacters()) { char ch = currentChar(); // is this an escape char? if (ch == '\\') { // because domain literals don't get extra escaping, we render them // with the escaped characters intact. Therefore, append the '\' escape // first, then append the escaped character without examination. value.append(currentChar()); // step past this, and grab the following character nextChar(); if (!moreCharacters()) { syntaxError("Missing '\"'", position); } value.append(currentChar()); } // end of the string? else if (ch == ']') { // return the constructed string. tokens.addToken(new AddressToken(value.toString(), DOMAIN_LITERAL, startPosition)); // step over the close delimiter for the benefit of the next token. nextChar(); return; } // the RFC822 spec says no nesting else if (ch == '[') { syntaxError("Unexpected '['", position); } // carriage returns are similarly illegal. else if (ch == '\r') { syntaxError("Illegal line end in domain literal", position); } else { value.append(ch); } nextChar(); } // missing delimiter syntaxError("Missing ']'", position); } /** * Scan an atom in an internet address, using the RFC822 rules * for atom delimiters. * * @param tokens The TokenStream where the parsed out token is added. */ private void scanAtom(TokenStream tokens) throws AddressException { int start = position; nextChar(); while (moreCharacters()) { char ch = currentChar(); if (isAtom(ch)) { nextChar(); } else { break; } } // return the scanned part of the string. tokens.addToken(new AddressToken(addresses.substring(start, position), ATOM, start)); } /** * Parse an internet address comment field as specified by * RFC822. Includes support for quoted characters and nesting. * * @param tokens The TokenStream where the parsed out token is added. */ private void scanComment(TokenStream tokens) throws AddressException { StringBuffer value = new StringBuffer(); int startPosition = position; // step past the start character nextChar(); // we're at the top nesting level on the comment. int nest = 1; // scan while we have more characters. while (moreCharacters()) { char ch = currentChar(); // escape character? if (ch == '\\') { // step over this...if escaped, we must have at least one more character // in the string. nextChar(); if (!moreCharacters()) { syntaxError("Missing ')'", position); } value.append(currentChar()); } // nested comment? else if (ch == '(') { // step the nesting level...we treat the comment as a single unit, with the delimiters // for the nested comments embedded in the middle nest++; value.append(ch); } // is this the comment close? else if (ch == ')') { // reduce the nesting level. If we still have more to process, add the delimiter character // and keep going. nest--; if (nest > 0) { value.append(ch); } else { // step past this and return. The outermost comment delimiter is not included in // the string value, since this is frequently used as personal data on the // InternetAddress objects. nextChar(); tokens.addToken(new AddressToken(value.toString(), COMMENT, startPosition)); return; } } else if (ch == '\r') { syntaxError("Illegal line end in comment", position); } else { value.append(ch); } // step to the next character. nextChar(); } // ran out of data before seeing the closing bit, not good syntaxError("Missing ')'", position); } /** * Validate the syntax of an RFC822 group internet address specification. * * @param tokens The stream of tokens for the address. * * @exception AddressException */ private void validateGroup(TokenStream tokens) throws AddressException { // we know already this is an address in the form "phrase:group;". Now we need to validate the // elements. int phraseCount = 0; AddressToken token = tokens.nextRealToken(); // now scan to the semi color, ensuring we have only word or comment tokens. while (token.type != COLON) { // only these tokens are allowed here. if (token.type != ATOM && token.type != QUOTED_LITERAL) { invalidToken(token); } phraseCount++; token = tokens.nextRealToken(); } // RFC822 groups require a leading phrase in group specifiers. if (phraseCount == 0) { illegalAddress("Missing group identifier phrase", token); } // now we do the remainder of the parsing using the initial phrase list as the sink...the entire // address will be converted to a string later. // ok, we only know this has been valid up to the ":", now we have some real checks to perform. while (true) { // go scan off a mailbox. if everything goes according to plan, we should be positioned at either // a comma or a semicolon. validateGroupMailbox(tokens); token = tokens.nextRealToken(); // we're at the end of the group. Make sure this is truely the end. if (token.type == SEMICOLON) { token = tokens.nextRealToken(); if (token.type != END_OF_TOKENS) { illegalAddress("Illegal group address", token); } return; } // if not a semicolon, this better be a comma. else if (token.type != COMMA) { illegalAddress("Illegal group address", token); } } } /** * Validate the syntax of single mailbox within a group address. * * @param tokens The stream of tokens representing the address. * * @exception AddressException */ private void validateGroupMailbox(TokenStream tokens) throws AddressException { AddressToken first = tokens.nextRealToken(); // is this just a null address in the list? then push the terminator back and return. if (first.type == COMMA || first.type == SEMICOLON) { tokens.pushToken(first); return; } // now we need to scan ahead to see if we can determine the type. AddressToken token = first; // we need to scan forward to figure out what sort of address this is. while (first != null) { switch (token.type) { // until we know the context, these are all just ignored. case QUOTED_LITERAL: case ATOM: break; // a LEFT_ANGLE indicates we have a full RFC822 mailbox form. The leading phrase // is the personal info. The address is inside the brackets. case LEFT_ANGLE: tokens.pushToken(first); validatePhrase(tokens, false); validateRouteAddr(tokens, true); return; // we've hit a period as the first non-word token. This should be part of a local-part // of an address. case PERIOD: // we've hit an "@" as the first non-word token. This is probably a simple address in // the form "user@domain". case AT_SIGN: tokens.pushToken(first); validateAddressSpec(tokens); return; // reached the end of string...this might be a null address, or one of the very simple name // forms used for non-strict RFC822 versions. Reset, and try that form case COMMA: // this is the end of the group...handle it like a comma for now. case SEMICOLON: tokens.pushToken(first); validateAddressSpec(tokens); return; case END_OF_TOKENS: illegalAddress("Missing ';'", token); } token = tokens.nextRealToken(); } } /** * Utility method for throwing an AddressException caused by an * unexpected primitive token. * * @param token The token causing the problem (must not be a value type token). * * @exception AddressException */ private void invalidToken(AddressToken token) throws AddressException { illegalAddress("Unexpected '" + token.type + "'", token); } /** * Raise an error about illegal syntax. * * @param message The message used in the thrown exception. * @param position The parsing position within the string. * * @exception AddressException */ private void syntaxError(String message, int position) throws AddressException { throw new AddressException(message, addresses, position); } /** * Throw an exception based on the position of an invalid token. * * @param message The exception message. * @param token The token causing the error. This tokens position is used * in the exception information. */ private void illegalAddress(String message, AddressToken token) throws AddressException { throw new AddressException(message, addresses, token.position); } /** * Validate that a required phrase exists. * * @param tokens The set of tokens to validate. positioned at the phrase start. * @param required A flag indicating whether the phrase is optional or required. * * @exception AddressException */ private void validatePhrase(TokenStream tokens, boolean required) throws AddressException { // we need to have at least one WORD token in the phrase...everything is optional // after that. AddressToken token = tokens.nextRealToken(); if (token.type != ATOM && token.type != QUOTED_LITERAL) { if (required) { illegalAddress("Missing group phrase", token); } } // now scan forward to the end of the phrase token = tokens.nextRealToken(); while (token.type == ATOM || token.type == QUOTED_LITERAL) { token = tokens.nextRealToken(); } } /** * validate a routeaddr specification * * @param tokens The tokens representing the address portion (personal information * already removed). * @param ingroup true indicates we're validating a route address inside a * group list. false indicates we're validating a standalone * address. * * @exception AddressException */ private void validateRouteAddr(TokenStream tokens, boolean ingroup) throws AddressException { // get the next real token. AddressToken token = tokens.nextRealToken(); // if this is an at sign, then we have a list of domains to parse. if (token.type == AT_SIGN) { // push the marker token back in for the route parser, and step past that part. tokens.pushToken(token); validateRoute(tokens); } else { // we need to push this back on to validate the local part. tokens.pushToken(token); } // now we expect to see an address spec. validateAddressSpec(tokens); token = tokens.nextRealToken(); if (ingroup) { // if we're validating within a group specification, the angle brackets are still there (and // required). if (token.type != RIGHT_ANGLE) { illegalAddress("Missing '>'", token); } } else { // the angle brackets were removed to make this an address, so we should be done. Make sure we // have a terminator here. if (token.type != END_OF_TOKENS) { illegalAddress("Illegal Address", token); } } } /** * Validate a simple address in the form "user@domain". * * @param tokens The stream of tokens representing the address. */ private void validateSimpleAddress(TokenStream tokens) throws AddressException { // the validation routines occur after addresses have been split into // personal and address forms. Therefore, our validation begins directly // with the first token. validateAddressSpec(tokens); // get the next token and see if there is something here...anything but the terminator is an error AddressToken token = tokens.nextRealToken(); if (token.type != END_OF_TOKENS) { illegalAddress("Illegal Address", token); } } /** * Validate the addr-spec portion of an address. RFC822 requires * this be of the form "local-part@domain". However, javamail also * allows simple address of the form "local-part". We only require * the domain if an '@' is encountered. * * @param tokens */ private void validateAddressSpec(TokenStream tokens) throws AddressException { // all addresses, even the simple ones, must have at least a local part. validateLocalPart(tokens); // now see if we have a domain portion to look at. AddressToken token = tokens.nextRealToken(); if (token.type == AT_SIGN) { validateDomain(tokens); } else { // put this back for termination tokens.pushToken(token); } } /** * Validate the route portion of a route-addr. This is a list * of domain values in the form 1#("@" domain) ":". * * @param tokens The token stream holding the address information. */ private void validateRoute(TokenStream tokens) throws AddressException { while (true) { AddressToken token = tokens.nextRealToken(); // if this is the first part of the list, go parse off a domain if (token.type == AT_SIGN) { validateDomain(tokens); } // another element in the list? Go around again else if (token.type == COMMA) { continue; } // the list is terminated by a colon...stop this part of the validation once we hit one. else if (token.type == COLON) { return; } // the list is terminated by a colon. If this isn't one of those, we have an error. else { illegalAddress("Missing ':'", token); } } } /** * Parse the local part of an address spec. The local part * is a series of "words" separated by ".". */ private void validateLocalPart(TokenStream tokens) throws AddressException { while (true) { // get the token. AddressToken token = tokens.nextRealToken(); // this must be either an atom or a literal. if (token.type != ATOM && token.type != QUOTED_LITERAL) { illegalAddress("Invalid local part", token); } // get the next token (white space and comments ignored) token = tokens.nextRealToken(); // if this is a period, we continue parsing if (token.type != PERIOD) { tokens.pushToken(token); // return the token return; } } } /** * Parse a domain name of the form sub-domain *("." sub-domain). * a sub-domain is either an atom or a domain-literal. */ private void validateDomain(TokenStream tokens) throws AddressException { while (true) { // get the token. AddressToken token = tokens.nextRealToken(); // this must be either an atom or a domain literal. if (token.type != ATOM && token.type != DOMAIN_LITERAL) { illegalAddress("Invalid domain", token); } // get the next token (white space is ignored) token = tokens.nextRealToken(); // if this is a period, we continue parsing if (token.type != PERIOD) { // return the token tokens.pushToken(token); return; } } } /** * Convert a list of word tokens into a phrase string. The * rules for this are a little hard to puzzle out, but there * is a logic to it. If the list is empty, the phrase is * just a null value. * * If we have a phrase, then the quoted strings need to * handled appropriately. In multi-token phrases, the * quoted literals are concatenated with the quotes intact, * regardless of content. Thus a phrase that comes in like this: * * "Geronimo" Apache * * gets converted back to the same string. * * If there is just a single token in the phrase, AND the token * is a quoted string AND the string does not contain embedded * special characters ("\.,@<>()[]:;), then the phrase * is expressed as an atom. Thus the literal * * "Geronimo" * * becomes * * Geronimo * * but * * "(Geronimo)" * * remains * * "(Geronimo)" * * Note that we're generating a canonical form of the phrase, * which removes comments and reduces linear whitespace down * to a single separator token. * * @param phrase An array list of phrase tokens (which may be empty). */ private String personalToString(TokenStream tokens) { // no tokens in the stream? This is a null value. AddressToken token = tokens.nextToken(); if (token.type == END_OF_TOKENS) { return null; } AddressToken next = tokens.nextToken(); // single element phrases get special treatment. if (next.type == END_OF_TOKENS) { // this can be used directly...if it contains special characters, quoting will be // performed when it's converted to a string value. return token.value; } // reset to the beginning tokens.pushToken(token); // have at least two tokens, StringBuffer buffer = new StringBuffer(); // get the first token. After the first, we add these as blank delimited values. token = tokens.nextToken(); addTokenValue(token, buffer); token = tokens.nextToken(); while (token.type != END_OF_TOKENS) { // add a blank separator buffer.append(' '); // now add the next tokens value addTokenValue(token, buffer); token = tokens.nextToken(); } // and return the canonicalized value return buffer.toString(); } /** * take a canonicalized set of address tokens and reformat it back into a string value, * inserting whitespace where appropriate. * * @param tokens The set of tokens representing the address. * * @return The string value of the tokens. */ private String addressToString(TokenStream tokens) { StringBuffer buffer = new StringBuffer(); // this flag controls whether we insert a blank delimiter between tokens as // we advance through the list. Blanks are only inserted between consequtive value tokens. // Initially, this is false, then we flip it to true whenever we add a value token, and // back to false for any special character token. boolean spaceRequired = false; // we use nextToken rather than nextRealToken(), since we need to process the comments also. AddressToken token = tokens.nextToken(); // now add each of the tokens while (token.type != END_OF_TOKENS) { switch (token.type) { // the word tokens are the only ones where we need to worry about adding // whitespace delimiters. case ATOM: case QUOTED_LITERAL: // was the last token also a word? Insert a blank first. if (spaceRequired) { buffer.append(' '); } addTokenValue(token, buffer); // let the next iteration know we just added a word to the list. spaceRequired = true; break; // these special characters are just added in. The constants for the character types // were carefully selected to be the character value in question. This allows us to // just append the value. case LEFT_ANGLE: case RIGHT_ANGLE: case COMMA: case COLON: case AT_SIGN: case SEMICOLON: case PERIOD: buffer.append((char)token.type); // no spaces around specials spaceRequired = false; break; // Domain literals self delimiting...we can just append them and turn off the space flag. case DOMAIN_LITERAL: addTokenValue(token, buffer); spaceRequired = false; break; // Comments are also self delimitin. case COMMENT: addTokenValue(token, buffer); spaceRequired = false; break; } token = tokens.nextToken(); } return buffer.toString(); } /** * Append a value token on to a string buffer used to create * the canonicalized string value. * * @param token The token we're adding. * @param buffer The target string buffer. */ private void addTokenValue(AddressToken token, StringBuffer buffer) { // atom values can be added directly. if (token.type == ATOM) { buffer.append(token.value); } // a literal value? Add this as a quoted string else if (token.type == QUOTED_LITERAL) { buffer.append(formatQuotedString(token.value)); } // could be a domain literal of the form "[value]" else if (token.type == DOMAIN_LITERAL) { buffer.append('['); buffer.append(token.value); buffer.append(']'); } // comments also have values else if (token.type == COMMENT) { buffer.append('('); buffer.append(token.value); buffer.append(')'); } } private static final byte[] CHARMAP = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06, 0x02, 0x06, 0x02, 0x02, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, }; private static final byte FLG_SPECIAL = 1; private static final byte FLG_CONTROL = 2; private static final byte FLG_SPACE = 4; private static boolean isSpace(char ch) { if (ch > '\u007f') { return false; } else { return (CHARMAP[ch] & FLG_SPACE) != 0; } } /** * Quick test to see if a character is an allowed atom character * or not. * * @param ch The test character. * * @return true if this character is allowed in atoms, false for any * control characters, special characters, or blanks. */ public static boolean isAtom(char ch) { if (ch > '\u007f') { return false; } else if (ch == ' ') { return false; } else { return (CHARMAP[ch] & (FLG_SPECIAL | FLG_CONTROL)) == 0; } } /** * Tests one string to determine if it contains any of the * characters in a supplied test string. * * @param s The string we're testing. * @param chars The set of characters we're testing against. * * @return true if any of the characters is found, false otherwise. */ public static boolean containsCharacters(String s, String chars) { for (int i = 0; i < s.length(); i++) { if (chars.indexOf(s.charAt(i)) >= 0) { return true; } } return false; } /** * Tests if a string contains any non-special characters that * would require encoding the value as a quoted string rather * than a simple atom value. * * @param s The test string. * * @return True if the string contains only blanks or allowed atom * characters. */ public static boolean containsSpecials(String s) { for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); // must be either a blank or an allowed atom char. if (ch == ' ' || isAtom(ch)) { continue; } else { return true; } } return false; } /** * Tests if a string contains any non-special characters that * would require encoding the value as a quoted string rather * than a simple atom value. * * @param s The test string. * * @return True if the string contains only blanks or allowed atom * characters. */ public static boolean isAtom(String s) { for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); // must be an allowed atom character if (!isAtom(ch)) { return false; } } return true; } /** * Apply RFC822 quoting rules to a literal string value. This * will search the string to see if there are any characters that * require special escaping, and apply the escapes. If the * string is just a string of blank-delimited atoms, the string * value is returned without quotes. * * @param s The source string. * * @return A version of the string as a valid RFC822 quoted literal. */ public static String quoteString(String s) { // only backslash and double quote require escaping. If the string does not // contain any of these, then we can just slap on some quotes and go. if (s.indexOf('\\') == -1 && s.indexOf('"') == -1) { // if the string is an atom (or a series of blank-delimited atoms), we can just return it directly. if (!containsSpecials(s)) { return s; } StringBuffer buffer = new StringBuffer(s.length() + 2); buffer.append('"'); buffer.append(s); buffer.append('"'); return buffer.toString(); } // get a buffer sufficiently large for the string, two quote characters, and a "reasonable" // number of escaped values. StringBuffer buffer = new StringBuffer(s.length() + 10); buffer.append('"'); // now check all of the characters. for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); // character requiring escaping? if (ch == '\\' || ch == '"') { // add an extra backslash buffer.append('\\'); } // and add on the character buffer.append(ch); } buffer.append('"'); return buffer.toString(); } /** * Apply RFC822 quoting rules to a literal string value. This * will search the string to see if there are any characters that * require special escaping, and apply the escapes. The returned * value is enclosed in quotes. * * @param s The source string. * * @return A version of the string as a valid RFC822 quoted literal. */ public static String formatQuotedString(String s) { // only backslash and double quote require escaping. If the string does not // contain any of these, then we can just slap on some quotes and go. if (s.indexOf('\\') == -1 && s.indexOf('"') == -1) { StringBuffer buffer = new StringBuffer(s.length() + 2); buffer.append('"'); buffer.append(s); buffer.append('"'); return buffer.toString(); } // get a buffer sufficiently large for the string, two quote characters, and a "reasonable" // number of escaped values. StringBuffer buffer = new StringBuffer(s.length() + 10); buffer.append('"'); // now check all of the characters. for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); // character requiring escaping? if (ch == '\\' || ch == '"') { // add an extra backslash buffer.append('\\'); } // and add on the character buffer.append(ch); } buffer.append('"'); return buffer.toString(); } public class TokenStream { // the set of tokens in the parsed address list, as determined by RFC822 syntax rules. private List tokens; // the current token position int currentToken = 0; /** * Default constructor for a TokenStream. This creates an * empty TokenStream for purposes of tokenizing an address. * It is the creator's responsibility to terminate the stream * with a terminator token. */ public TokenStream() { tokens = new ArrayList(); } /** * Construct a TokenStream from a list of tokens. A terminator * token is added to the end. * * @param tokens An existing token list. */ public TokenStream(List tokens) { this.tokens = tokens; tokens.add(new AddressToken(END_OF_TOKENS, -1)); } /** * Add an address token to the token list. * * @param t The new token to add to the list. */ public void addToken(AddressToken token) { tokens.add(token); } /** * Get the next token at the cursor position, advancing the * position accordingly. * * @return The token at the current token position. */ public AddressToken nextToken() { AddressToken token = (AddressToken)tokens.get(currentToken++); // we skip over white space tokens when operating in this mode, so // check the token and iterate until we get a non-white space. while (token.type == WHITESPACE) { token = (AddressToken)tokens.get(currentToken++); } return token; } /** * Get the next token at the cursor position, without advancing the * position. * * @return The token at the current token position. */ public AddressToken currentToken() { // return the current token and step the cursor return (AddressToken)tokens.get(currentToken); } /** * Get the next non-comment token from the string. Comments are ignored, except as personal information * for very simple address specifications. * * @return A token guaranteed not to be a whitespace token. */ public AddressToken nextRealToken() { AddressToken token = nextToken(); if (token.type == COMMENT) { token = nextToken(); } return token; } /** * Push a token back on to the queue, making the index of this * token the current cursor position. * * @param token The token to push. */ public void pushToken(AddressToken token) { // just reset the cursor to the token's index position. currentToken = tokenIndex(token); } /** * Get the next token after a given token, without advancing the * token position. * * @param token The token we're retrieving a token relative to. * * @return The next token in the list. */ public AddressToken nextToken(AddressToken token) { return (AddressToken)tokens.get(tokenIndex(token) + 1); } /** * Return the token prior to a given token. * * @param token The token used for the index. * * @return The token prior to the index token in the list. */ public AddressToken previousToken(AddressToken token) { return (AddressToken)tokens.get(tokenIndex(token) - 1); } /** * Retrieve a token at a given index position. * * @param index The target index. */ public AddressToken getToken(int index) { return (AddressToken)tokens.get(index); } /** * Retrieve the index of a particular token in the stream. * * @param token The target token. * * @return The index of the token within the stream. Returns -1 if this * token is somehow not in the stream. */ public int tokenIndex(AddressToken token) { return tokens.indexOf(token); } /** * Extract a new TokenStream running from the start token to the * token preceeding the end token. * * @param start The starting token of the section. * @param end The last token (+1) for the target section. * * @return A new TokenStream object for processing this section of tokens. */ public TokenStream section(AddressToken start, AddressToken end) { int startIndex = tokenIndex(start); int endIndex = tokenIndex(end); // List.subList() returns a list backed by the original list. Since we need to add a // terminator token to this list when we take the sublist, we need to manually copy the // references so we don't end up munging the original list. ArrayList list = new ArrayList(endIndex - startIndex + 2); for (int i = startIndex; i <= endIndex; i++) { list.add(tokens.get(i)); } return new TokenStream(list); } /** * Reset the token position back to the beginning of the * stream. */ public void reset() { currentToken = 0; } /** * Scan forward looking for a non-blank token. * * @return The first non-blank token in the stream. */ public AddressToken getNonBlank() { AddressToken token = currentToken(); while (token.type == WHITESPACE) { currentToken++; token = currentToken(); } return token; } /** * Extract a blank delimited token from a TokenStream. A blank * delimited token is the set of tokens up to the next real whitespace * token (comments not included). * * @return A TokenStream object with the new set of tokens. */ public TokenStream getBlankDelimitedToken() { // get the next non-whitespace token. AddressToken first = getNonBlank(); // if this is the end, we return null. if (first.type == END_OF_TOKENS) { return null; } AddressToken last = first; // the methods for retrieving tokens skip over whitespace, so we're going to process this // by index. currentToken++; AddressToken token = currentToken(); while (true) { // if this is our marker, then pluck out the section and return it. if (token.type == END_OF_TOKENS || token.type == WHITESPACE) { return section(first, last); } last = token; currentToken++; // we accept any and all tokens here. token = currentToken(); } } /** * Return the index of the current cursor position. * * @return The integer index of the current token. */ public int currentIndex() { return currentToken; } public void dumpTokens() { System.out.println(">>>>>>>>> Start dumping TokenStream tokens"); for (int i = 0; i < tokens.size(); i++) { System.out.println("-------- Token: " + tokens.get(i)); } System.out.println("++++++++ cursor position=" + currentToken); System.out.println(">>>>>>>>> End dumping TokenStream tokens"); } } /** * Simple utility class for representing address tokens. */ public class AddressToken { // the token type int type; // string value of the token (can be null) String value; // position of the token within the address string. int position; AddressToken(int type, int position) { this.type = type; this.value = null; this.position = position; } AddressToken(String value, int type, int position) { this.type = type; this.value = value; this.position = position; } public String toString() { if (type == END_OF_TOKENS) { return "AddressToken: type=END_OF_TOKENS"; } if (value == null) { return "AddressToken: type=" + (char)type; } else { return "AddressToken: type=" + (char)type + " value=" + value; } } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/HeaderTokenizer.java0000664000175000017500000002113111124341735027567 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; /** * @version $Rev: 729233 $ $Date: 2008-12-24 00:08:45 -0500 (Wed, 24 Dec 2008) $ */ public class HeaderTokenizer { public static class Token { // Constant values from J2SE 1.4 API Docs (Constant values) public static final int ATOM = -1; public static final int COMMENT = -3; public static final int EOF = -4; public static final int QUOTEDSTRING = -2; private int _type; private String _value; public Token(int type, String value) { _type = type; _value = value; } public int getType() { return _type; } public String getValue() { return _value; } } private static final Token EOF = new Token(Token.EOF, null); // characters not allowed in MIME public static final String MIME = "()<>@,;:\\\"\t []/?="; // charaters not allowed in RFC822 public static final String RFC822 = "()<>@,;:\\\"\t .[]"; private static final String WHITE = " \t\n\r"; private String _delimiters; private String _header; private boolean _skip; private int pos; public HeaderTokenizer(String header) { this(header, RFC822); } public HeaderTokenizer(String header, String delimiters) { this(header, delimiters, true); } public HeaderTokenizer(String header, String delimiters, boolean skipComments) { _skip = skipComments; _header = header; _delimiters = delimiters; } public String getRemainder() { return _header.substring(pos); } public Token next() throws ParseException { return readToken(); } public Token peek() throws ParseException { int start = pos; try { return readToken(); } finally { pos = start; } } /** * Read an ATOM token from the parsed header. * * @return A token containing the value of the atom token. */ private Token readAtomicToken() { // skip to next delimiter int start = pos; while (++pos < _header.length()) { // break on the first non-atom character. char ch = _header.charAt(pos); if (_delimiters.indexOf(_header.charAt(pos)) != -1 || ch < 32 || ch >= 127) { break; } } return new Token(Token.ATOM, _header.substring(start, pos)); } /** * Read the next token from the header. * * @return The next token from the header. White space is skipped, and comment * tokens are also skipped if indicated. * @exception ParseException */ private Token readToken() throws ParseException { if (pos >= _header.length()) { return EOF; } else { char c = _header.charAt(pos); // comment token...read and skip over this if (c == '(') { Token comment = readComment(); if (_skip) { return readToken(); } else { return comment; } // quoted literal } else if (c == '\"') { return readQuotedString(); // white space, eat this and find a real token. } else if (WHITE.indexOf(c) != -1) { eatWhiteSpace(); return readToken(); // either a CTL or special. These characters have a self-defining token type. } else if (c < 32 || c >= 127 || _delimiters.indexOf(c) != -1) { pos++; return new Token((int)c, String.valueOf(c)); } else { // start of an atom, parse it off. return readAtomicToken(); } } } /** * Extract a substring from the header string and apply any * escaping/folding rules to the string. * * @param start The starting offset in the header. * @param end The header end offset + 1. * * @return The processed string value. * @exception ParseException */ private String getEscapedValue(int start, int end) throws ParseException { StringBuffer value = new StringBuffer(); for (int i = start; i < end; i++) { char ch = _header.charAt(i); // is this an escape character? if (ch == '\\') { i++; if (i == end) { throw new ParseException("Invalid escape character"); } value.append(_header.charAt(i)); } // line breaks are ignored, except for naked '\n' characters, which are consider // parts of linear whitespace. else if (ch == '\r') { // see if this is a CRLF sequence, and skip the second if it is. if (i < end - 1 && _header.charAt(i + 1) == '\n') { i++; } } else { // just append the ch value. value.append(ch); } } return value.toString(); } /** * Read a comment from the header, applying nesting and escape * rules to the content. * * @return A comment token with the token value. * @exception ParseException */ private Token readComment() throws ParseException { int start = pos + 1; int nesting = 1; boolean requiresEscaping = false; // skip to end of comment/string while (++pos < _header.length()) { char ch = _header.charAt(pos); if (ch == ')') { nesting--; if (nesting == 0) { break; } } else if (ch == '(') { nesting++; } else if (ch == '\\') { pos++; requiresEscaping = true; } // we need to process line breaks also else if (ch == '\r') { requiresEscaping = true; } } if (nesting != 0) { throw new ParseException("Unbalanced comments"); } String value; if (requiresEscaping) { value = getEscapedValue(start, pos); } else { value = _header.substring(start, pos++); } return new Token(Token.COMMENT, value); } /** * Parse out a quoted string from the header, applying escaping * rules to the value. * * @return The QUOTEDSTRING token with the value. * @exception ParseException */ private Token readQuotedString() throws ParseException { int start = pos+1; boolean requiresEscaping = false; // skip to end of comment/string while (++pos < _header.length()) { char ch = _header.charAt(pos); if (ch == '"') { String value; if (requiresEscaping) { value = getEscapedValue(start, pos++); } else { value = _header.substring(start, pos++); } return new Token(Token.QUOTEDSTRING, value); } else if (ch == '\\') { pos++; requiresEscaping = true; } // we need to process line breaks also else if (ch == '\r') { requiresEscaping = true; } } throw new ParseException("Missing '\"'"); } /** * Skip white space in the token string. */ private void eatWhiteSpace() { // skip to end of whitespace while (++pos < _header.length() && WHITE.indexOf(_header.charAt(pos)) != -1) ; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/ParameterList.java0000664000175000017500000002666311404403313027267 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList;// Represents lists in things like import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.apache.geronimo.mail.util.ASCIIUtil; import org.apache.geronimo.mail.util.RFC2231Encoder; import org.apache.geronimo.mail.util.SessionUtil; // Content-Type: text/plain;charset=klingon // // The ;charset=klingon is the parameter list, may have more of them with ';' /** * @version $Rev: 953633 $ $Date: 2010-06-11 05:50:03 -0400 (Fri, 11 Jun 2010) $ */ public class ParameterList { private static final String MIME_ENCODEPARAMETERS = "mail.mime.encodeparameters"; private static final String MIME_DECODEPARAMETERS = "mail.mime.decodeparameters"; private static final String MIME_DECODEPARAMETERS_STRICT = "mail.mime.decodeparameters.strict"; private static final int HEADER_SIZE_LIMIT = 76; private Map _parameters = new HashMap(); private boolean encodeParameters = false; private boolean decodeParameters = false; private boolean decodeParametersStrict = false; public ParameterList() { // figure out how parameter handling is to be performed. getInitialProperties(); } public ParameterList(String list) throws ParseException { // figure out how parameter handling is to be performed. getInitialProperties(); // get a token parser for the type information HeaderTokenizer tokenizer = new HeaderTokenizer(list, HeaderTokenizer.MIME); while (true) { HeaderTokenizer.Token token = tokenizer.next(); switch (token.getType()) { // the EOF token terminates parsing. case HeaderTokenizer.Token.EOF: return; // each new parameter is separated by a semicolon, including the first, which separates // the parameters from the main part of the header. case ';': // the next token needs to be a parameter name token = tokenizer.next(); // allow a trailing semicolon on the parameters. if (token.getType() == HeaderTokenizer.Token.EOF) { return; } if (token.getType() != HeaderTokenizer.Token.ATOM) { throw new ParseException("Invalid parameter name: " + token.getValue()); } // get the parameter name as a lower case version for better mapping. String name = token.getValue().toLowerCase(); token = tokenizer.next(); // parameters are name=value, so we must have the "=" here. if (token.getType() != '=') { throw new ParseException("Missing '='"); } // now the value, which may be an atom or a literal token = tokenizer.next(); if (token.getType() != HeaderTokenizer.Token.ATOM && token.getType() != HeaderTokenizer.Token.QUOTEDSTRING) { throw new ParseException("Invalid parameter value: " + token.getValue()); } String value = token.getValue(); String decodedValue = null; // we might have to do some additional decoding. A name that ends with "*" // is marked as being encoded, so if requested, we decode the value. if (decodeParameters && name.endsWith("*")) { // the name needs to be pruned of the marker, and we need to decode the value. name = name.substring(0, name.length() - 1); // get a new decoder RFC2231Encoder decoder = new RFC2231Encoder(HeaderTokenizer.MIME); try { // decode the value decodedValue = decoder.decode(value); } catch (Exception e) { // if we're doing things strictly, then raise a parsing exception for errors. // otherwise, leave the value in its current state. if (decodeParametersStrict) { throw new ParseException("Invalid RFC2231 encoded parameter"); } } _parameters.put(name, new ParameterValue(name, decodedValue, value)); } else { _parameters.put(name, new ParameterValue(name, value)); } break; default: throw new ParseException("Missing ';'"); } } } /** * Get the initial parameters that control parsing and values. * These parameters are controlled by System properties. */ private void getInitialProperties() { decodeParameters = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS, false); decodeParametersStrict = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS_STRICT, false); encodeParameters = SessionUtil.getBooleanProperty(MIME_ENCODEPARAMETERS, true); } public int size() { return _parameters.size(); } public String get(String name) { ParameterValue value = (ParameterValue)_parameters.get(name.toLowerCase()); if (value != null) { return value.value; } return null; } public void set(String name, String value) { name = name.toLowerCase(); _parameters.put(name, new ParameterValue(name, value)); } public void set(String name, String value, String charset) { name = name.toLowerCase(); // only encode if told to and this contains non-ASCII charactes. if (encodeParameters && !ASCIIUtil.isAscii(value)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { RFC2231Encoder encoder = new RFC2231Encoder(HeaderTokenizer.MIME); // extract the bytes using the given character set and encode byte[] valueBytes = value.getBytes(MimeUtility.javaCharset(charset)); // the string format is charset''data out.write(charset.getBytes("ISO8859-1")); out.write('\''); out.write('\''); encoder.encode(valueBytes, 0, valueBytes.length, out); // default in case there is an exception _parameters.put(name, new ParameterValue(name, value, new String(out.toByteArray(), "ISO8859-1"))); return; } catch (Exception e) { // just fall through and set the value directly if there is an error } } // default in case there is an exception _parameters.put(name, new ParameterValue(name, value)); } public void remove(String name) { _parameters.remove(name); } public Enumeration getNames() { return Collections.enumeration(_parameters.keySet()); } public String toString() { // we need to perform folding, but out starting point is 0. return toString(0); } public String toString(int used) { StringBuffer stringValue = new StringBuffer(); Iterator values = _parameters.values().iterator(); while (values.hasNext()) { ParameterValue parm = (ParameterValue)values.next(); // get the values we're going to encode in here. String name = parm.getEncodedName(); String value = parm.toString(); // add the semicolon separator. We also add a blank so that folding/unfolding rules can be used. stringValue.append("; "); used += 2; // too big for the current header line? if ((used + name.length() + value.length() + 1) > HEADER_SIZE_LIMIT) { // and a CRLF-combo combo. stringValue.append("\r\n\t"); // reset the counter for a fresh line // note we use use 8 because we're using a rather than a blank used = 8; } // now add the keyword/value pair. stringValue.append(name); stringValue.append("="); used += name.length() + 1; // we're not out of the woods yet. It is possible that the keyword/value pair by itself might // be too long for a single line. If that's the case, the we need to fold the value, if possible if (used + value.length() > HEADER_SIZE_LIMIT) { String foldedValue = MimeUtility.fold(used, value); stringValue.append(foldedValue); // now we need to sort out how much of the current line is in use. int lastLineBreak = foldedValue.lastIndexOf('\n'); if (lastLineBreak != -1) { used = foldedValue.length() - lastLineBreak + 1; } else { used += foldedValue.length(); } } else { // no folding required, just append. stringValue.append(value); used += value.length(); } } return stringValue.toString(); } /** * Utility class for representing parameter values in the list. */ class ParameterValue { public String name; // the name of the parameter public String value; // the original set value public String encodedValue; // an encoded value, if encoding is requested. public ParameterValue(String name, String value) { this.name = name; this.value = value; this.encodedValue = null; } public ParameterValue(String name, String value, String encodedValue) { this.name = name; this.value = value; this.encodedValue = encodedValue; } public String toString() { if (encodedValue != null) { return MimeUtility.quote(encodedValue, HeaderTokenizer.MIME); } return MimeUtility.quote(value, HeaderTokenizer.MIME); } public String getEncodedName() { if (encodedValue != null) { return name + "*"; } return name; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/InternetHeaders.java0000664000175000017500000005652411375023416027607 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.mail.Address; import javax.mail.Header; import javax.mail.MessagingException; /** * Class that represents the RFC822 headers associated with a message. * * @version $Rev: 946312 $ $Date: 2010-05-19 13:59:42 -0400 (Wed, 19 May 2010) $ */ public class InternetHeaders { // the list of headers (to preserve order); protected List headers = new ArrayList(); private transient String lastHeaderName; /** * Create an empty InternetHeaders */ public InternetHeaders() { // these are created in the preferred order of the headers. addHeader("Return-Path", null); addHeader("Received", null); addHeader("Resent-Date", null); addHeader("Resent-From", null); addHeader("Resent-Sender", null); addHeader("Resent-To", null); addHeader("Resent-Cc", null); addHeader("Resent-Bcc", null); addHeader("Resent-Message-Id", null); addHeader("Date", null); addHeader("From", null); addHeader("Sender", null); addHeader("Reply-To", null); addHeader("To", null); addHeader("Cc", null); addHeader("Bcc", null); addHeader("Message-Id", null); addHeader("In-Reply-To", null); addHeader("References", null); addHeader("Subject", null); addHeader("Comments", null); addHeader("Keywords", null); addHeader("Errors-To", null); addHeader("MIME-Version", null); addHeader("Content-Type", null); addHeader("Content-Transfer-Encoding", null); addHeader("Content-MD5", null); // the following is a special marker used to identify new header insertion points. addHeader(":", null); addHeader("Content-Length", null); addHeader("Status", null); } /** * Create a new InternetHeaders initialized by reading headers from the * stream. * * @param in * the RFC822 input stream to load from * @throws MessagingException * if there is a problem pasring the stream */ public InternetHeaders(InputStream in) throws MessagingException { load(in); } /** * Read and parse the supplied stream and add all headers to the current * set. * * @param in * the RFC822 input stream to load from * @throws MessagingException * if there is a problem pasring the stream */ public void load(InputStream in) throws MessagingException { try { StringBuffer buffer = new StringBuffer(128); String line; // loop until we hit the end or a null line while ((line = readLine(in)) != null) { // lines beginning with white space get special handling if (line.startsWith(" ") || line.startsWith("\t")) { // this gets handled using the logic defined by // the addHeaderLine method. If this line is a continuation, but // there's nothing before it, just call addHeaderLine to add it // to the last header in the headers list if (buffer.length() == 0) { addHeaderLine(line); } else { // preserve the line break and append the continuation buffer.append("\r\n"); buffer.append(line); } } else { // if we have a line pending in the buffer, flush it if (buffer.length() > 0) { addHeaderLine(buffer.toString()); buffer.setLength(0); } // add this to the accumulator buffer.append(line); } } // if we have a line pending in the buffer, flush it if (buffer.length() > 0) { addHeaderLine(buffer.toString()); } } catch (IOException e) { throw new MessagingException("Error loading headers", e); } } /** * Read a single line from the input stream * * @param in The source stream for the line * * @return The string value of the line (without line separators) */ private String readLine(InputStream in) throws IOException { StringBuffer buffer = new StringBuffer(128); int c; while ((c = in.read()) != -1) { // a linefeed is a terminator, always. if (c == '\n') { break; } // just ignore the CR. The next character SHOULD be an NL. If not, we're // just going to discard this else if (c == '\r') { continue; } else { // just add to the buffer buffer.append((char)c); } } // no characters found...this was either an eof or a null line. if (buffer.length() == 0) { return null; } return buffer.toString(); } /** * Return all the values for the specified header. * * @param name * the header to return * @return the values for that header, or null if the header is not present */ public String[] getHeader(String name) { List accumulator = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); if (header.getName().equalsIgnoreCase(name) && header.getValue() != null) { accumulator.add(header.getValue()); } } // this is defined as returning null of nothing is found. if (accumulator.isEmpty()) { return null; } // convert this to an array. return (String[])accumulator.toArray(new String[accumulator.size()]); } /** * Return the values for the specified header as a single String. If the * header has more than one value then all values are concatenated together * separated by the supplied delimiter. * * @param name * the header to return * @param delimiter * the delimiter used in concatenation * @return the header as a single String */ public String getHeader(String name, String delimiter) { // get all of the headers with this name String[] matches = getHeader(name); // no match? return a null. if (matches == null) { return null; } // a null delimiter means just return the first one. If there's only one item, this is easy too. if (matches.length == 1 || delimiter == null) { return matches[0]; } // perform the concatenation StringBuffer result = new StringBuffer(matches[0]); for (int i = 1; i < matches.length; i++) { result.append(delimiter); result.append(matches[i]); } return result.toString(); } /** * Set the value of the header to the supplied value; any existing headers * are removed. * * @param name * the name of the header * @param value * the new value */ public void setHeader(String name, String value) { // look for a header match for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // found a matching header if (name.equalsIgnoreCase(header.getName())) { // we update both the name and the value for a set so that // the header ends up with the same case as what is getting set header.setValue(value); header.setName(name); // remove all of the headers from this point removeHeaders(name, i + 1); return; } } // doesn't exist, so process as an add. addHeader(name, value); } /** * Remove all headers with the given name, starting with the * specified start position. * * @param name The target header name. * @param pos The position of the first header to examine. */ private void removeHeaders(String name, int pos) { // now go remove all other instances of this header for (int i = pos; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // found a matching header if (name.equalsIgnoreCase(header.getName())) { // remove this item, and back up headers.remove(i); i--; } } } /** * Find a header in the current list by name, returning the index. * * @param name The target name. * * @return The index of the header in the list. Returns -1 for a not found * condition. */ private int findHeader(String name) { return findHeader(name, 0); } /** * Find a header in the current list, beginning with the specified * start index. * * @param name The target header name. * @param start The search start index. * * @return The index of the first matching header. Returns -1 if the * header is not located. */ private int findHeader(String name, int start) { for (int i = start; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // found a matching header if (name.equalsIgnoreCase(header.getName())) { return i; } } return -1; } /** * Add a new value to the header with the supplied name. * * @param name * the name of the header to add a new value for * @param value * another value */ public void addHeader(String name, String value) { InternetHeader newHeader = new InternetHeader(name, value); // The javamail spec states that "Recieved" headers need to be added in reverse order. // Return-Path is permitted before Received, so handle it the same way. if (name.equalsIgnoreCase("Received") || name.equalsIgnoreCase("Return-Path")) { // see if we have one of these already int pos = findHeader(name); // either insert before an existing header, or insert at the very beginning if (pos != -1) { // this could be a placeholder header with a null value. If it is, just update // the value. Otherwise, insert in front of the existing header. InternetHeader oldHeader = (InternetHeader)headers.get(pos); if (oldHeader.getValue() == null) { oldHeader.setValue(value); } else { headers.add(pos, newHeader); } } else { // doesn't exist, so insert at the beginning headers.add(0, newHeader); } } // normal insertion else { // see if we have one of these already int pos = findHeader(name); // either insert before an existing header, or insert at the very beginning if (pos != -1) { InternetHeader oldHeader = (InternetHeader)headers.get(pos); // if the existing header is a place holder, we can just update the value if (oldHeader.getValue() == null) { oldHeader.setValue(value); } else { // we have at least one existing header with this name. We need to find the last occurrance, // and insert after that spot. int lastPos = findHeader(name, pos + 1); while (lastPos != -1) { pos = lastPos; lastPos = findHeader(name, pos + 1); } // ok, we have the insertion position headers.add(pos + 1, newHeader); } } else { // find the insertion marker. If that is missing somehow, insert at the end. pos = findHeader(":"); if (pos == -1) { pos = headers.size(); } headers.add(pos, newHeader); } } } /** * Remove all header entries with the supplied name * * @param name * the header to remove */ public void removeHeader(String name) { // the first occurrance of a header is just zeroed out. int pos = findHeader(name); if (pos != -1) { InternetHeader oldHeader = (InternetHeader)headers.get(pos); // keep the header in the list, but with a null value oldHeader.setValue(null); // now remove all other headers with this name removeHeaders(name, pos + 1); } } /** * Return all headers. * * @return an Enumeration

containing all headers */ public Enumeration getAllHeaders() { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { result.add(header); } } // just return a list enumerator for the header list. return Collections.enumeration(result); } /** * Test if a given header name is a match for any header in the * given list. * * @param name The name of the current tested header. * @param names The list of names to match against. * * @return True if this is a match for any name in the list, false * for a complete mismatch. */ private boolean matchHeader(String name, String[] names) { // the list of names is not required, so treat this as if it // was an empty list and we didn't get a match. if (names == null) { return false; } for (int i = 0; i < names.length; i++) { if (name.equalsIgnoreCase(names[i])) { return true; } } return false; } /** * Return all matching Header objects. */ public Enumeration getMatchingHeaders(String[] names) { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { // only add the matching ones if (matchHeader(header.getName(), names)) { result.add(header); } } } return Collections.enumeration(result); } /** * Return all non matching Header objects. */ public Enumeration getNonMatchingHeaders(String[] names) { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { // only add the non-matching ones if (!matchHeader(header.getName(), names)) { result.add(header); } } } return Collections.enumeration(result); } /** * Add an RFC822 header line to the header store. If the line starts with a * space or tab (a continuation line), add it to the last header line in the * list. Otherwise, append the new header line to the list. * * Note that RFC822 headers can only contain US-ASCII characters * * @param line * raw RFC822 header line */ public void addHeaderLine(String line) { // null lines are a nop if (line.length() == 0) { return; } // we need to test the first character to see if this is a continuation whitespace char ch = line.charAt(0); // tabs and spaces are special. This is a continuation of the last header in the list. if (ch == ' ' || ch == '\t') { int size = headers.size(); // it's possible that we have a leading blank line. if (size > 0) { InternetHeader header = (InternetHeader)headers.get(size - 1); header.appendValue(line); } } else { // this just gets appended to the end, preserving the addition order. headers.add(new InternetHeader(line)); } } /** * Return all the header lines as an Enumeration of Strings. */ public Enumeration getAllHeaderLines() { return new HeaderLineEnumeration(getAllHeaders()); } /** * Return all matching header lines as an Enumeration of Strings. */ public Enumeration getMatchingHeaderLines(String[] names) { return new HeaderLineEnumeration(getMatchingHeaders(names)); } /** * Return all non-matching header lines. */ public Enumeration getNonMatchingHeaderLines(String[] names) { return new HeaderLineEnumeration(getNonMatchingHeaders(names)); } /** * Set an internet header from a list of addresses. The * first address item is set, followed by a series of addHeaders(). * * @param name The name to set. * @param addresses The list of addresses to set. */ void setHeader(String name, Address[] addresses) { // if this is empty, then we need to replace this if (addresses.length == 0) { removeHeader(name); } else { // replace the first header setHeader(name, addresses[0].toString()); // now add the rest as extra headers. for (int i = 1; i < addresses.length; i++) { Address address = addresses[i]; addHeader(name, address.toString()); } } } /** * Write out the set of headers, except for any * headers specified in the optional ignore list. * * @param out The output stream. * @param ignore The optional ignore list. * * @exception IOException */ void writeTo(OutputStream out, String[] ignore) throws IOException { if (ignore == null) { // write out all header lines with non-null values for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { header.writeTo(out); } } } else { // write out all matching header lines with non-null values for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { if (!matchHeader(header.getName(), ignore)) { header.writeTo(out); } } } } } protected static final class InternetHeader extends Header { public InternetHeader(String h) { // initialize with null values, which we'll update once we parse the string super("", ""); int separator = h.indexOf(':'); // no separator, then we take this as a name with a null string value. if (separator == -1) { name = h.trim(); } else { name = h.substring(0, separator); // step past the separator. Now we need to remove any leading white space characters. separator++; while (separator < h.length()) { char ch = h.charAt(separator); if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') { break; } separator++; } value = h.substring(separator); } } public InternetHeader(String name, String value) { super(name, value); } /** * Package scope method for setting the header value. * * @param value The new header value. */ void setValue(String value) { this.value = value; } /** * Package scope method for setting the name value. * * @param name The new header name */ void setName(String name) { this.name = name; } /** * Package scope method for extending a header value. * * @param value The appended header value. */ void appendValue(String value) { if (this.value == null) { this.value = value; } else { this.value = this.value + "\r\n" + value; } } void writeTo(OutputStream out) throws IOException { out.write(name.getBytes("ISO8859-1")); out.write(':'); out.write(' '); out.write(value.getBytes("ISO8859-1")); out.write('\r'); out.write('\n'); } } private static class HeaderLineEnumeration implements Enumeration { private Enumeration headers; public HeaderLineEnumeration(Enumeration headers) { this.headers = headers; } public boolean hasMoreElements() { return headers.hasMoreElements(); } public Object nextElement() { Header h = (Header) headers.nextElement(); return h.getName() + ": " + h.getValue(); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimePartDataSource.java0000664000175000017500000001050011160451621030167 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.UnknownServiceException; import javax.activation.DataSource; import javax.mail.MessageAware; import javax.mail.MessageContext; import javax.mail.MessagingException; /** * @version $Rev: 756013 $ $Date: 2009-03-19 10:15:45 -0400 (Thu, 19 Mar 2009) $ */ public class MimePartDataSource implements DataSource, MessageAware { // the part that provides the data form this data source. protected MimePart part; public MimePartDataSource(MimePart part) { this.part = part; } public InputStream getInputStream() throws IOException { try { InputStream stream; if (part instanceof MimeMessage) { stream = ((MimeMessage) part).getContentStream(); } else if (part instanceof MimeBodyPart) { stream = ((MimeBodyPart) part).getContentStream(); } else { throw new MessagingException("Unknown part"); } return checkPartEncoding(part, stream); } catch (MessagingException e) { throw (IOException) new IOException(e.getMessage()).initCause(e); } } /** * For a given part, decide it the data stream requires * wrappering with a stream for decoding a particular * encoding. * * @param part The part we're extracting. * @param stream The raw input stream for the part. * * @return An input stream configured for reading the * source part and decoding it into raw bytes. */ private InputStream checkPartEncoding(MimePart part, InputStream stream) throws MessagingException { String encoding = part.getEncoding(); // if nothing is specified, there's nothing to do if (encoding == null) { return stream; } // now screen out the ones that never need decoding encoding = encoding.toLowerCase(); if (encoding.equals("7bit") || encoding.equals("8bit") || encoding.equals("binary")) { return stream; } // now we need to check the content type to prevent // MultiPart types from getting decoded, since the part is just an envelope around other // parts String contentType = part.getContentType(); if (contentType != null) { try { ContentType type = new ContentType(contentType); // no decoding done here if (type.match("multipart/*")) { return stream; } } catch (ParseException e) { // ignored....bad content type means we handle as a normal part } } // ok, wrap this is a decoding stream if required return MimeUtility.decode(stream, encoding); } public OutputStream getOutputStream() throws IOException { throw new UnknownServiceException(); } public String getContentType() { try { return part.getContentType(); } catch (MessagingException e) { return null; } } public String getName() { try { if (part instanceof MimeBodyPart) { return ((MimeBodyPart) part).getFileName(); } } catch (MessagingException mex) { // ignore it } return ""; } public synchronized MessageContext getMessageContext() { return new MessageContext(part); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimeBodyPart.java0000664000175000017500000006073611055026261027053 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.HeaderTokenizer.Token; import javax.swing.text.AbstractDocument.Content; import org.apache.geronimo.mail.util.ASCIIUtil; import org.apache.geronimo.mail.util.SessionUtil; /** * @version $Rev: 689126 $ $Date: 2008-08-26 12:17:53 -0400 (Tue, 26 Aug 2008) $ */ public class MimeBodyPart extends BodyPart implements MimePart { // constants for accessed properties private static final String MIME_DECODEFILENAME = "mail.mime.decodefilename"; private static final String MIME_ENCODEFILENAME = "mail.mime.encodefilename"; private static final String MIME_SETDEFAULTTEXTCHARSET = "mail.mime.setdefaulttextcharset"; private static final String MIME_SETCONTENTTYPEFILENAME = "mail.mime.setcontenttypefilename"; /** * The {@link DataHandler} for this Message's content. */ protected DataHandler dh; /** * This message's content (unless sourced from a SharedInputStream). */ protected byte content[]; /** * If the data for this message was supplied by a {@link SharedInputStream} * then this is another such stream representing the content of this message; * if this field is non-null, then {@link #content} will be null. */ protected InputStream contentStream; /** * This message's headers. */ protected InternetHeaders headers; public MimeBodyPart() { headers = new InternetHeaders(); } public MimeBodyPart(InputStream in) throws MessagingException { headers = new InternetHeaders(in); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int count; try { while((count = in.read(buffer, 0, 1024)) > 0) { baos.write(buffer, 0, count); } } catch (IOException e) { throw new MessagingException(e.toString(),e); } content = baos.toByteArray(); } public MimeBodyPart(InternetHeaders headers, byte[] content) throws MessagingException { this.headers = headers; this.content = content; } /** * Return the content size of this message. This is obtained * either from the size of the content field (if available) or * from the contentStream, IFF the contentStream returns a positive * size. Returns -1 if the size is not available. * * @return Size of the content in bytes. * @exception MessagingException */ public int getSize() throws MessagingException { if (content != null) { return content.length; } if (contentStream != null) { try { int size = contentStream.available(); if (size > 0) { return size; } } catch (IOException e) { } } return -1; } public int getLineCount() throws MessagingException { return -1; } public String getContentType() throws MessagingException { String value = getSingleHeader("Content-Type"); if (value == null) { value = "text/plain"; } return value; } /** * Tests to see if this message has a mime-type match with the * given type name. * * @param type The tested type name. * * @return If this is a type match on the primary and secondare portion of the types. * @exception MessagingException */ public boolean isMimeType(String type) throws MessagingException { return new ContentType(getContentType()).match(type); } /** * Retrieve the message "Content-Disposition" header field. * This value represents how the part should be represented to * the user. * * @return The string value of the Content-Disposition field. * @exception MessagingException */ public String getDisposition() throws MessagingException { String disp = getSingleHeader("Content-Disposition"); if (disp != null) { return new ContentDisposition(disp).getDisposition(); } return null; } /** * Set a new dispostion value for the "Content-Disposition" field. * If the new value is null, the header is removed. * * @param disposition * The new disposition value. * * @exception MessagingException */ public void setDisposition(String disposition) throws MessagingException { if (disposition == null) { removeHeader("Content-Disposition"); } else { // the disposition has parameters, which we'll attempt to preserve in any existing header. String currentHeader = getSingleHeader("Content-Disposition"); if (currentHeader != null) { ContentDisposition content = new ContentDisposition(currentHeader); content.setDisposition(disposition); setHeader("Content-Disposition", content.toString()); } else { // set using the raw string. setHeader("Content-Disposition", disposition); } } } /** * Retrieves the current value of the "Content-Transfer-Encoding" * header. Returns null if the header does not exist. * * @return The current header value or null. * @exception MessagingException */ public String getEncoding() throws MessagingException { // this might require some parsing to sort out. String encoding = getSingleHeader("Content-Transfer-Encoding"); if (encoding != null) { // we need to parse this into ATOMs and other constituent parts. We want the first // ATOM token on the string. HeaderTokenizer tokenizer = new HeaderTokenizer(encoding, HeaderTokenizer.MIME); Token token = tokenizer.next(); while (token.getType() != Token.EOF) { // if this is an ATOM type, return it. if (token.getType() == Token.ATOM) { return token.getValue(); } } // not ATOMs found, just return the entire header value....somebody might be able to make sense of // this. return encoding; } // no header, nothing to return. return null; } /** * Retrieve the value of the "Content-ID" header. Returns null * if the header does not exist. * * @return The current header value or null. * @exception MessagingException */ public String getContentID() throws MessagingException { return getSingleHeader("Content-ID"); } public void setContentID(String cid) throws MessagingException { setOrRemoveHeader("Content-ID", cid); } public String getContentMD5() throws MessagingException { return getSingleHeader("Content-MD5"); } public void setContentMD5(String md5) throws MessagingException { setHeader("Content-MD5", md5); } public String[] getContentLanguage() throws MessagingException { return getHeader("Content-Language"); } public void setContentLanguage(String[] languages) throws MessagingException { if (languages == null) { removeHeader("Content-Language"); } else if (languages.length == 1) { setHeader("Content-Language", languages[0]); } else { StringBuffer buf = new StringBuffer(languages.length * 20); buf.append(languages[0]); for (int i = 1; i < languages.length; i++) { buf.append(',').append(languages[i]); } setHeader("Content-Language", buf.toString()); } } public String getDescription() throws MessagingException { String description = getSingleHeader("Content-Description"); if (description != null) { try { // this could be both folded and encoded. Return this to usable form. return MimeUtility.decodeText(MimeUtility.unfold(description)); } catch (UnsupportedEncodingException e) { // ignore } } // return the raw version for any errors. return description; } public void setDescription(String description) throws MessagingException { setDescription(description, null); } public void setDescription(String description, String charset) throws MessagingException { if (description == null) { removeHeader("Content-Description"); } else { try { setHeader("Content-Description", MimeUtility.fold(21, MimeUtility.encodeText(description, charset, null))); } catch (UnsupportedEncodingException e) { throw new MessagingException(e.getMessage(), e); } } } public String getFileName() throws MessagingException { // see if there is a disposition. If there is, parse off the filename parameter. String disposition = getSingleHeader("Content-Disposition"); String filename = null; if (disposition != null) { filename = new ContentDisposition(disposition).getParameter("filename"); } // if there's no filename on the disposition, there might be a name parameter on a // Content-Type header. if (filename == null) { String type = getSingleHeader("Content-Type"); if (type != null) { try { filename = new ContentType(type).getParameter("name"); } catch (ParseException e) { } } } // if we have a name, we might need to decode this if an additional property is set. if (filename != null && SessionUtil.getBooleanProperty(MIME_DECODEFILENAME, false)) { try { filename = MimeUtility.decodeText(filename); } catch (UnsupportedEncodingException e) { throw new MessagingException("Unable to decode filename", e); } } return filename; } public void setFileName(String name) throws MessagingException { // there's an optional session property that requests file name encoding...we need to process this before // setting the value. if (name != null && SessionUtil.getBooleanProperty(MIME_ENCODEFILENAME, false)) { try { name = MimeUtility.encodeText(name); } catch (UnsupportedEncodingException e) { throw new MessagingException("Unable to encode filename", e); } } // get the disposition string. String disposition = getDisposition(); // if not there, then this is an attachment. if (disposition == null) { disposition = Part.ATTACHMENT; } // now create a disposition object and set the parameter. ContentDisposition contentDisposition = new ContentDisposition(disposition); contentDisposition.setParameter("filename", name); // serialize this back out and reset. setHeader("Content-Disposition", contentDisposition.toString()); // The Sun implementation appears to update the Content-type name parameter too, based on // another system property if (SessionUtil.getBooleanProperty(MIME_SETCONTENTTYPEFILENAME, true)) { ContentType type = new ContentType(getContentType()); type.setParameter("name", name); setHeader("Content-Type", type.toString()); } } public InputStream getInputStream() throws MessagingException, IOException { return getDataHandler().getInputStream(); } protected InputStream getContentStream() throws MessagingException { if (contentStream != null) { return contentStream; } if (content != null) { return new ByteArrayInputStream(content); } else { throw new MessagingException("No content"); } } public InputStream getRawInputStream() throws MessagingException { return getContentStream(); } public synchronized DataHandler getDataHandler() throws MessagingException { if (dh == null) { dh = new DataHandler(new MimePartDataSource(this)); } return dh; } public Object getContent() throws MessagingException, IOException { return getDataHandler().getContent(); } public void setDataHandler(DataHandler handler) throws MessagingException { dh = handler; // if we have a handler override, then we need to invalidate any content // headers that define the types. This information will be derived from the // data heander unless subsequently overridden. removeHeader("Content-Type"); removeHeader("Content-Transfer-Encoding"); } public void setContent(Object content, String type) throws MessagingException { // Multipart content needs to be handled separately. if (content instanceof Multipart) { setContent((Multipart)content); } else { setDataHandler(new DataHandler(content, type)); } } public void setText(String text) throws MessagingException { setText(text, null); } public void setText(String text, String charset) throws MessagingException { // the default subtype is plain text. setText(text, charset, "plain"); } public void setText(String text, String charset, String subtype) throws MessagingException { // we need to sort out the character set if one is not provided. if (charset == null) { // if we have non us-ascii characters here, we need to adjust this. if (!ASCIIUtil.isAscii(text)) { charset = MimeUtility.getDefaultMIMECharset(); } else { charset = "us-ascii"; } } setContent(text, "text/plain; charset=" + MimeUtility.quote(charset, HeaderTokenizer.MIME)); } public void setContent(Multipart part) throws MessagingException { setDataHandler(new DataHandler(part, part.getContentType())); part.setParent(this); } public void writeTo(OutputStream out) throws IOException, MessagingException { headers.writeTo(out, null); // add the separater between the headers and the data portion. out.write('\r'); out.write('\n'); // we need to process this using the transfer encoding type OutputStream encodingStream = MimeUtility.encode(out, getEncoding()); getDataHandler().writeTo(encodingStream); encodingStream.flush(); } public String[] getHeader(String name) throws MessagingException { return headers.getHeader(name); } public String getHeader(String name, String delimiter) throws MessagingException { return headers.getHeader(name, delimiter); } public void setHeader(String name, String value) throws MessagingException { headers.setHeader(name, value); } /** * Conditionally set or remove a named header. If the new value * is null, the header is removed. * * @param name The header name. * @param value The new header value. A null value causes the header to be * removed. * * @exception MessagingException */ private void setOrRemoveHeader(String name, String value) throws MessagingException { if (value == null) { headers.removeHeader(name); } else { headers.setHeader(name, value); } } public void addHeader(String name, String value) throws MessagingException { headers.addHeader(name, value); } public void removeHeader(String name) throws MessagingException { headers.removeHeader(name); } public Enumeration getAllHeaders() throws MessagingException { return headers.getAllHeaders(); } public Enumeration getMatchingHeaders(String[] name) throws MessagingException { return headers.getMatchingHeaders(name); } public Enumeration getNonMatchingHeaders(String[] name) throws MessagingException { return headers.getNonMatchingHeaders(name); } public void addHeaderLine(String line) throws MessagingException { headers.addHeaderLine(line); } public Enumeration getAllHeaderLines() throws MessagingException { return headers.getAllHeaderLines(); } public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException { return headers.getMatchingHeaderLines(names); } public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException { return headers.getNonMatchingHeaderLines(names); } protected void updateHeaders() throws MessagingException { DataHandler handler = getDataHandler(); try { // figure out the content type. If not set, we'll need to figure this out. String type = dh.getContentType(); // parse this content type out so we can do matches/compares. ContentType content = new ContentType(type); // we might need to reconcile the content type and our explicitly set type String explicitType = getSingleHeader("Content-Type"); // is this a multipart content? if (content.match("multipart/*")) { // the content is suppose to be a MimeMultipart. Ping it to update it's headers as well. try { MimeMultipart part = (MimeMultipart)handler.getContent(); part.updateHeaders(); } catch (ClassCastException e) { throw new MessagingException("Message content is not MimeMultipart", e); } } else if (!content.match("message/rfc822")) { // simple part, we need to update the header type information // if no encoding is set yet, figure this out from the data handler. if (getSingleHeader("Content-Transfer-Encoding") == null) { setHeader("Content-Transfer-Encoding", MimeUtility.getEncoding(handler)); } // is a content type header set? Check the property to see if we need to set this. if (explicitType == null) { if (SessionUtil.getBooleanProperty(MIME_SETDEFAULTTEXTCHARSET, true)) { // is this a text type? Figure out the encoding and make sure it is set. if (content.match("text/*")) { // the charset should be specified as a parameter on the MIME type. If not there, // try to figure one out. if (content.getParameter("charset") == null) { String encoding = getEncoding(); // if we're sending this as 7-bit ASCII, our character set need to be // compatible. if (encoding != null && encoding.equalsIgnoreCase("7bit")) { content.setParameter("charset", "us-ascii"); } else { // get the global default. content.setParameter("charset", MimeUtility.getDefaultMIMECharset()); } // replace the datasource provided type type = content.toString(); } } } } } // if we don't have a content type header, then create one. if (explicitType == null) { // get the disposition header, and if it is there, copy the filename parameter into the // name parameter of the type. String disp = getHeader("Content-Disposition", null); if (disp != null) { // parse up the string value of the disposition ContentDisposition disposition = new ContentDisposition(disp); // now check for a filename value String filename = disposition.getParameter("filename"); // copy and rename the parameter, if it exists. if (filename != null) { content.setParameter("name", filename); // and update the string version type = content.toString(); } } // set the header with the updated content type information. setHeader("Content-Type", type); } } catch (IOException e) { throw new MessagingException("Error updating message headers", e); } } private String getSingleHeader(String name) throws MessagingException { String[] values = getHeader(name); if (values == null || values.length == 0) { return null; } else { return values[0]; } } /** * Attach a file to this body part from a File object. * * @param file The source File object. * * @exception IOException * @exception MessagingException */ public void attachFile(File file) throws IOException, MessagingException { FileDataSource dataSource = new FileDataSource(file); setDataHandler(new DataHandler(dataSource)); setFileName(dataSource.getName()); } /** * Attach a file to this body part from a file name. * * @param file The source file name. * * @exception IOException * @exception MessagingException */ public void attachFile(String file) throws IOException, MessagingException { // just create a File object and attach. attachFile(new File(file)); } /** * Save the body part content to a given target file. * * @param file The File object used to store the information. * * @exception IOException * @exception MessagingException */ public void saveFile(File file) throws IOException, MessagingException { OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); // we need to read the data in to write it out (sigh). InputStream in = getInputStream(); try { byte[] buffer = new byte[8192]; int length; while ((length = in.read(buffer)) > 0) { out.write(buffer, 0, length); } } finally { // make sure all of the streams are closed before we return if (in != null) { in.close(); } if (out != null) { out.close(); } } } /** * Save the body part content to a given target file. * * @param file The file name used to store the information. * * @exception IOException * @exception MessagingException */ public void saveFile(String file) throws IOException, MessagingException { saveFile(new File(file)); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/ContentDisposition.java0000664000175000017500000000663611026434562030362 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; // http://www.faqs.org/rfcs/rfc2183.html /** * @version $Rev: 669445 $ $Date: 2008-06-19 06:48:18 -0400 (Thu, 19 Jun 2008) $ */ public class ContentDisposition { private String _disposition; private ParameterList _list; public ContentDisposition() { setDisposition(null); setParameterList(null); } public ContentDisposition(String disposition) throws ParseException { // get a token parser for the type information HeaderTokenizer tokenizer = new HeaderTokenizer(disposition, HeaderTokenizer.MIME); // get the first token, which must be an ATOM HeaderTokenizer.Token token = tokenizer.next(); if (token.getType() != HeaderTokenizer.Token.ATOM) { throw new ParseException("Invalid content disposition"); } _disposition = token.getValue(); // the remainder is parameters, which ParameterList will take care of parsing. String remainder = tokenizer.getRemainder(); if (remainder != null) { _list = new ParameterList(remainder); } } public ContentDisposition(String disposition, ParameterList list) { setDisposition(disposition); setParameterList(list); } public String getDisposition() { return _disposition; } public String getParameter(String name) { if (_list == null) { return null; } else { return _list.get(name); } } public ParameterList getParameterList() { return _list; } public void setDisposition(String string) { _disposition = string; } public void setParameter(String name, String value) { if (_list == null) { _list = new ParameterList(); } _list.set(name, value); } public void setParameterList(ParameterList list) { if (list == null) { _list = new ParameterList(); } else { _list = list; } } public String toString() { // it is possible we might have a parameter list, but this is meaningless if // there is no disposition string. Return a failure. if (_disposition == null) { return null; } // no parameter list? Just return the disposition string if (_list == null) { return _disposition; } // format this for use on a Content-Disposition header, which means we need to // account for the length of the header part too. return _disposition + _list.toString("Content-Disposition".length() + _disposition.length()); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/internet/MimePart.java0000664000175000017500000000444210517560657026243 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.util.Enumeration; import javax.mail.MessagingException; import javax.mail.Part; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface MimePart extends Part { public abstract void addHeaderLine(String line) throws MessagingException; public abstract Enumeration getAllHeaderLines() throws MessagingException; public abstract String getContentID() throws MessagingException; public abstract String[] getContentLanguage() throws MessagingException; public abstract String getContentMD5() throws MessagingException; public abstract String getEncoding() throws MessagingException; public abstract String getHeader(String header, String delimiter) throws MessagingException; public abstract Enumeration getMatchingHeaderLines(String[] names) throws MessagingException; public abstract Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException; public abstract void setContentLanguage(String[] languages) throws MessagingException; public abstract void setContentMD5(String content) throws MessagingException; public abstract void setText(String text) throws MessagingException; public abstract void setText(String text, String charset) throws MessagingException; public abstract void setText(String text, String charset, String subType) throws MessagingException; } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/SendFailedException.java0000664000175000017500000000402711345370365026544 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class SendFailedException extends MessagingException { private static final long serialVersionUID = -6457531621682372913L; protected transient Address invalid[]; protected transient Address validSent[]; protected transient Address validUnsent[]; public SendFailedException() { super(); } public SendFailedException(String message) { super(message); } public SendFailedException(String message, Exception cause) { super(message, cause); } public SendFailedException(String message, Exception cause, Address[] validSent, Address[] validUnsent, Address[] invalid) { this(message, cause); this.invalid = invalid; this.validSent = validSent; this.validUnsent = validUnsent; } public Address[] getValidSentAddresses() { return validSent; } public Address[] getValidUnsentAddresses() { return validUnsent; } public Address[] getInvalidAddresses() { return invalid; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Message.java0000664000175000017500000003621610675734274024270 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Date; import javax.mail.search.SearchTerm; /** * @version $Rev: 578802 $ $Date: 2007-09-24 09:16:44 -0400 (Mon, 24 Sep 2007) $ */ public abstract class Message implements Part { /** * Enumeration of types of recipients allowed by the Message class. */ public static class RecipientType implements Serializable { /** * A "To" or primary recipient. */ public static final RecipientType TO = new RecipientType("To"); /** * A "Cc" or carbon-copy recipient. */ public static final RecipientType CC = new RecipientType("Cc"); /** * A "Bcc" or blind carbon-copy recipient. */ public static final RecipientType BCC = new RecipientType("Bcc"); protected String type; protected RecipientType(String type) { this.type = type; } protected Object readResolve() throws ObjectStreamException { if (type.equals("To")) { return TO; } else if (type.equals("Cc")) { return CC; } else if (type.equals("Bcc")) { return BCC; } else { throw new InvalidObjectException("Invalid RecipientType: " + type); } } public String toString() { return type; } } /** * The index of a message within its folder, or zero if the message was not retrieved from a folder. */ protected int msgnum; /** * True if this message has been expunged from the Store. */ protected boolean expunged; /** * The {@link Folder} that contains this message, or null if it was not obtained from a folder. */ protected Folder folder; /** * The {@link Session} associated with this message. */ protected Session session; /** * Default constructor. */ protected Message() { } /** * Constructor initializing folder and message msgnum; intended to be used by implementations of Folder. * * @param folder the folder that contains the message * @param msgnum the message index within the folder */ protected Message(Folder folder, int msgnum) { this.folder = folder; this.msgnum = msgnum; // make sure we copy the session information from the folder. this.session = folder.getStore().getSession(); } /** * Constructor initializing the session; intended to by used by client created instances. * * @param session the session associated with this message */ protected Message(Session session) { this.session = session; } /** * Return the "From" header indicating the identity of the person the message is from; * in some circumstances this may be different than the actual sender. * * @return a list of addresses this message is from; may be empty if the header is present but empty, or null * if the header is not present * @throws MessagingException if there was a problem accessing the Store */ public abstract Address[] getFrom() throws MessagingException; /** * Set the "From" header for this message to the value of the "mail.user" property, * or if that property is not set, to the value of the system property "user.name" * * @throws MessagingException if there was a problem accessing the Store */ public abstract void setFrom() throws MessagingException; /** * Set the "From" header to the supplied address. * * @param address the address of the person the message is from * @throws MessagingException if there was a problem accessing the Store */ public abstract void setFrom(Address address) throws MessagingException; /** * Add multiple addresses to the "From" header. * * @param addresses the addresses to add * @throws MessagingException if there was a problem accessing the Store */ public abstract void addFrom(Address[] addresses) throws MessagingException; /** * Get all recipients of the given type. * * @param type the type of recipient to get * @return a list of addresses; may be empty if the header is present but empty, * or null if the header is not present * @throws MessagingException if there was a problem accessing the Store * @see RecipientType */ public abstract Address[] getRecipients(RecipientType type) throws MessagingException; /** * Get all recipients of this message. * The default implementation extracts the To, Cc, and Bcc recipients using {@link #getRecipients(javax.mail.Message.RecipientType)} * and then concatentates the results into a single array; it returns null if no headers are defined. * * @return an array containing all recipients * @throws MessagingException if there was a problem accessing the Store */ public Address[] getAllRecipients() throws MessagingException { Address[] to = getRecipients(RecipientType.TO); Address[] cc = getRecipients(RecipientType.CC); Address[] bcc = getRecipients(RecipientType.BCC); if (to == null && cc == null && bcc == null) { return null; } int length = (to != null ? to.length : 0) + (cc != null ? cc.length : 0) + (bcc != null ? bcc.length : 0); Address[] result = new Address[length]; int j = 0; if (to != null) { for (int i = 0; i < to.length; i++) { result[j++] = to[i]; } } if (cc != null) { for (int i = 0; i < cc.length; i++) { result[j++] = cc[i]; } } if (bcc != null) { for (int i = 0; i < bcc.length; i++) { result[j++] = bcc[i]; } } return result; } /** * Set the list of recipients for the specified type. * * @param type the type of recipient * @param addresses the new addresses * @throws MessagingException if there was a problem accessing the Store */ public abstract void setRecipients(RecipientType type, Address[] addresses) throws MessagingException; /** * Set the list of recipients for the specified type to a single address. * * @param type the type of recipient * @param address the new address * @throws MessagingException if there was a problem accessing the Store */ public void setRecipient(RecipientType type, Address address) throws MessagingException { setRecipients(type, new Address[]{address}); } /** * Add recipents of a specified type. * * @param type the type of recipient * @param addresses the addresses to add * @throws MessagingException if there was a problem accessing the Store */ public abstract void addRecipients(RecipientType type, Address[] addresses) throws MessagingException; /** * Add a recipent of a specified type. * * @param type the type of recipient * @param address the address to add * @throws MessagingException if there was a problem accessing the Store */ public void addRecipient(RecipientType type, Address address) throws MessagingException { addRecipients(type, new Address[]{address}); } /** * Get the addresses to which replies should be directed. *

* As the most common behavior is to return to sender, the default implementation * simply calls {@link #getFrom()}. * * @return a list of addresses to which replies should be directed * @throws MessagingException if there was a problem accessing the Store */ public Address[] getReplyTo() throws MessagingException { return getFrom(); } /** * Set the addresses to which replies should be directed. *

* The default implementation throws a MethodNotSupportedException. * * @param addresses to which replies should be directed * @throws MessagingException if there was a problem accessing the Store */ public void setReplyTo(Address[] addresses) throws MessagingException { throw new MethodNotSupportedException("setReplyTo not supported"); } /** * Get the subject for this message. * * @return the subject * @throws MessagingException if there was a problem accessing the Store */ public abstract String getSubject() throws MessagingException; /** * Set the subject of this message * * @param subject the subject * @throws MessagingException if there was a problem accessing the Store */ public abstract void setSubject(String subject) throws MessagingException; /** * Return the date that this message was sent. * * @return the date this message was sent * @throws MessagingException if there was a problem accessing the Store */ public abstract Date getSentDate() throws MessagingException; /** * Set the date this message was sent. * * @param sent the date when this message was sent * @throws MessagingException if there was a problem accessing the Store */ public abstract void setSentDate(Date sent) throws MessagingException; /** * Return the date this message was received. * * @return the date this message was received * @throws MessagingException if there was a problem accessing the Store */ public abstract Date getReceivedDate() throws MessagingException; /** * Return a copy the flags associated with this message. * * @return a copy of the flags for this message * @throws MessagingException if there was a problem accessing the Store */ public abstract Flags getFlags() throws MessagingException; /** * Check whether the supplied flag is set. * The default implementation checks the flags returned by {@link #getFlags()}. * * @param flag the flags to check for * @return true if the flags is set * @throws MessagingException if there was a problem accessing the Store */ public boolean isSet(Flags.Flag flag) throws MessagingException { return getFlags().contains(flag); } /** * Set the flags specified to the supplied value; flags not included in the * supplied {@link Flags} parameter are not affected. * * @param flags the flags to modify * @param set the new value of those flags * @throws MessagingException if there was a problem accessing the Store */ public abstract void setFlags(Flags flags, boolean set) throws MessagingException; /** * Set a flag to the supplied value. * The default implmentation uses {@link #setFlags(Flags, boolean)}. * * @param flag the flag to set * @param set the value for that flag * @throws MessagingException if there was a problem accessing the Store */ public void setFlag(Flags.Flag flag, boolean set) throws MessagingException { setFlags(new Flags(flag), set); } /** * Return the message number for this Message. * This number refers to the relative position of this message in a Folder; the message * number for any given message can change during a session if the Folder is expunged. * Message numbers for messages in a folder start at one; the value zero indicates that * this message does not belong to a folder. * * @return the message number */ public int getMessageNumber() { return msgnum; } /** * Set the message number for this Message. * This must be invoked by implementation classes when the message number changes. * * @param number the new message number */ protected void setMessageNumber(int number) { msgnum = number; } /** * Return the folder containing this message. If this is a new or nested message * then this method returns null. * * @return the folder containing this message */ public Folder getFolder() { return folder; } /** * Checks to see if this message has been expunged. If true, all methods other than * {@link #getMessageNumber()} are invalid. * * @return true if this method has been expunged */ public boolean isExpunged() { return expunged; } /** * Set the expunged flag for this message. * * @param expunged true if this message has been expunged */ protected void setExpunged(boolean expunged) { this.expunged = expunged; } /** * Create a new message suitable as a reply to this message with all headers set * up appropriately. The message body will be empty. *

* if replyToAll is set then the new message will be addressed to all recipients * of this message; otherwise the reply will be addressed only to the sender as * returned by {@link #getReplyTo()}. *

* The subject field will be initialized with the subject field from the orginal * message; the text "Re:" will be prepended unless it is already present. * * @param replyToAll if true, indciates the message should be addressed to all recipients not just the sender * @return a new message suitable as a reply to this message * @throws MessagingException if there was a problem accessing the Store */ public abstract Message reply(boolean replyToAll) throws MessagingException; /** * To ensure changes are saved to the Store, this message should be invoked * before its containing Folder is closed. Implementations may save modifications * immediately but are free to defer such updates to they may be sent to the server * in one batch; if saveChanges is not called then such changes may not be made * permanent. * * @throws MessagingException if there was a problem accessing the Store */ public abstract void saveChanges() throws MessagingException; /** * Apply the specified search criteria to this message * * @param term the search criteria * @return true if this message matches the search criteria. * @throws MessagingException if there was a problem accessing the Store */ public boolean match(SearchTerm term) throws MessagingException { return term.match(this); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MessageAware.java0000664000175000017500000000176010517560657025241 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface MessageAware { public abstract MessageContext getMessageContext(); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MessagingException.java0000664000175000017500000000425111345370365026462 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MessagingException extends Exception { private static final long serialVersionUID = -7569192289819959253L; // Required because serialization expects it to be here private Exception next; public MessagingException() { super(); } public MessagingException(String message) { super(message); } public MessagingException(String message, Exception cause) { super(message, cause); next = cause; } public Exception getNextException() { return next; } public synchronized boolean setNextException(Exception cause) { if (next == null) { initCause(cause); next = cause; return true; } else if (next instanceof MessagingException) { return ((MessagingException) next).setNextException(cause); } else { return false; } } public String getMessage() { Exception next = getNextException(); if (next == null) { return super.getMessage(); } else { return super.getMessage() + " (" + next.getClass().getName() + ": " + next.getMessage() + ")"; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Folder.java0000664000175000017500000007243610702410673024104 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.ArrayList; import java.util.List; import javax.mail.Flags.Flag; import javax.mail.event.ConnectionEvent; import javax.mail.event.ConnectionListener; import javax.mail.event.FolderEvent; import javax.mail.event.FolderListener; import javax.mail.event.MailEvent; import javax.mail.event.MessageChangedEvent; import javax.mail.event.MessageChangedListener; import javax.mail.event.MessageCountEvent; import javax.mail.event.MessageCountListener; import javax.mail.search.SearchTerm; /** * An abstract representation of a folder in a mail system; subclasses would * implement Folders for each supported protocol. *

* Depending on protocol and implementation, folders may contain other folders, messages, * or both as indicated by the {@link Folder#HOLDS_FOLDERS} and {@link Folder#HOLDS_MESSAGES} flags. * If the immplementation supports hierarchical folders, the format of folder names is * implementation dependent; however, components of the name are separated by the * delimiter character returned by {@link Folder#getSeparator()}. *

* The case-insensitive folder name "INBOX" is reserved to refer to the primary folder * for the current user on the current server; not all stores will provide an INBOX * and it may not be available at all times. * * @version $Rev: 582780 $ $Date: 2007-10-08 07:17:15 -0400 (Mon, 08 Oct 2007) $ */ public abstract class Folder { /** * Flag that indicates that a folder can contain messages. */ public static final int HOLDS_MESSAGES = 1; /** * Flag that indicates that a folder can contain other folders. */ public static final int HOLDS_FOLDERS = 2; /** * Flag indicating that this folder cannot be modified. */ public static final int READ_ONLY = 1; /** * Flag indictaing that this folder can be modified. * Question: what does it mean if both are set? */ public static final int READ_WRITE = 2; /** * The store that this folder is part of. */ protected Store store; /** * The current mode of this folder. * When open, this can be {@link #READ_ONLY} or {@link #READ_WRITE}; * otherwise is set to -1. */ protected int mode = -1; private final ArrayList connectionListeners = new ArrayList(2); private final ArrayList folderListeners = new ArrayList(2); private final ArrayList messageChangedListeners = new ArrayList(2); private final ArrayList messageCountListeners = new ArrayList(2); // the EventQueue spins off a new thread, so we only create this // if we have actual listeners to dispatch an event to. private EventQueue queue = null; /** * Constructor that initializes the Store. * * @param store the store that this folder is part of */ protected Folder(Store store) { this.store = store; } /** * Return the name of this folder. * This can be invoked when the folder is closed. * * @return this folder's name */ public abstract String getName(); /** * Return the full absolute name of this folder. * This can be invoked when the folder is closed. * * @return the full name of this folder */ public abstract String getFullName(); /** * Return the URLName for this folder, which includes the location of the store. * * @return the URLName for this folder * @throws MessagingException */ public URLName getURLName() throws MessagingException { URLName baseURL = store.getURLName(); return new URLName(baseURL.getProtocol(), baseURL.getHost(), baseURL.getPort(), getFullName(), baseURL.getUsername(), null); } /** * Return the store that this folder is part of. * * @return the store this folder is part of */ public Store getStore() { return store; } /** * Return the parent for this folder; if the folder is at the root of a heirarchy * this returns null. * This can be invoked when the folder is closed. * * @return this folder's parent * @throws MessagingException */ public abstract Folder getParent() throws MessagingException; /** * Check to see if this folder physically exists in the store. * This can be invoked when the folder is closed. * * @return true if the folder really exists * @throws MessagingException if there was a problem accessing the store */ public abstract boolean exists() throws MessagingException; /** * Return a list of folders from this Folder's namespace that match the supplied pattern. * Patterns may contain the following wildcards: *

  • '%' which matches any characater except hierarchy delimiters
  • *
  • '*' which matches any character including hierarchy delimiters
  • *
* This can be invoked when the folder is closed. * * @param pattern the pattern to search for * @return a, possibly empty, array containing Folders that matched the pattern * @throws MessagingException if there was a problem accessing the store */ public abstract Folder[] list(String pattern) throws MessagingException; /** * Return a list of folders to which the user is subscribed and which match the supplied pattern. * If the store does not support the concept of subscription then this should match against * all folders; the default implementation of this method achieves this by defaulting to the * {@link #list(String)} method. * * @param pattern the pattern to search for * @return a, possibly empty, array containing subscribed Folders that matched the pattern * @throws MessagingException if there was a problem accessing the store */ public Folder[] listSubscribed(String pattern) throws MessagingException { return list(pattern); } /** * Convenience method that invokes {@link #list(String)} with the pattern "%". * * @return a, possibly empty, array of subfolders * @throws MessagingException if there was a problem accessing the store */ public Folder[] list() throws MessagingException { return list("%"); } /** * Convenience method that invokes {@link #listSubscribed(String)} with the pattern "%". * * @return a, possibly empty, array of subscribed subfolders * @throws MessagingException if there was a problem accessing the store */ public Folder[] listSubscribed() throws MessagingException { return listSubscribed("%"); } /** * Return the character used by this folder's Store to separate path components. * * @return the name separater character * @throws MessagingException if there was a problem accessing the store */ public abstract char getSeparator() throws MessagingException; /** * Return the type of this folder, indicating whether it can contain subfolders, * messages, or both. The value returned is a bitmask with the appropriate bits set. * * @return the type of this folder * @throws MessagingException if there was a problem accessing the store * @see #HOLDS_FOLDERS * @see #HOLDS_MESSAGES */ public abstract int getType() throws MessagingException; /** * Create a new folder capable of containing subfoldera and/or messages as * determined by the type parameter. Any hierarchy defined by the folder * name will be recursively created. * If the folder was sucessfully created, a {@link FolderEvent#CREATED CREATED FolderEvent} * is sent to all FolderListeners registered with this Folder or with the Store. * * @param type the type, indicating if this folder should contain subfolders, messages or both * @return true if the folder was sucessfully created * @throws MessagingException if there was a problem accessing the store */ public abstract boolean create(int type) throws MessagingException; /** * Determine if the user is subscribed to this Folder. The default implementation in * this class always returns true. * * @return true is the user is subscribed to this Folder */ public boolean isSubscribed() { return true; } /** * Set the user's subscription to this folder. * Not all Stores support subscription; the default implementation in this class * always throws a MethodNotSupportedException * * @param subscribed whether to subscribe to this Folder * @throws MessagingException if there was a problem accessing the store * @throws MethodNotSupportedException if the Store does not support subscription */ public void setSubscribed(boolean subscribed) throws MessagingException { throw new MethodNotSupportedException(); } /** * Check to see if this Folder conatins messages with the {@link Flag.RECENT} flag set. * This can be used when the folder is closed to perform a light-weight check for new mail; * to perform an incremental check for new mail the folder must be opened. * * @return true if the Store has recent messages * @throws MessagingException if there was a problem accessing the store */ public abstract boolean hasNewMessages() throws MessagingException; /** * Get the Folder determined by the supplied name; if the name is relative * then it is interpreted relative to this folder. This does not check that * the named folder actually exists. * * @param name the name of the folder to return * @return the named folder * @throws MessagingException if there was a problem accessing the store */ public abstract Folder getFolder(String name) throws MessagingException; /** * Delete this folder and possibly any subfolders. This operation can only be * performed on a closed folder. * If recurse is true, then all subfolders are deleted first, then any messages in * this folder are removed and it is finally deleted; {@link FolderEvent#DELETED} * events are sent as appropriate. * If recurse is false, then the behaviour depends on the folder type and store * implementation as followd: *
    *
  • If the folder can only conrain messages, then all messages are removed and * then the folder is deleted; a {@link FolderEvent#DELETED} event is sent.
  • *
  • If the folder can onlu contain subfolders, then if it is empty it will be * deleted and a {@link FolderEvent#DELETED} event is sent; if the folder is not * empty then the delete fails and this method returns false.
  • *
  • If the folder can contain both subfolders and messages, then if the folder * does not contain any subfolders, any messages are deleted, the folder itself * is deleted and a {@link FolderEvent#DELETED} event is sent; if the folder does * contain subfolders then the implementation may choose from the following three * behaviors: *
      *
    1. it may return false indicting the operation failed
    2. *
    3. it may remove all messages within the folder, send a {@link FolderEvent#DELETED} * event, and then return true to indicate the delete was performed. Note this does * not delete the folder itself and the {@link #exists()} operation for this folder * will return true
    4. *
    5. it may remove all messages within the folder as per the previous option; in * addition it may change the type of the Folder to only HOLDS_FOLDERS indictaing * that messages may no longer be added
    6. * *
* FolderEvents are sent to all listeners registered with this folder or * with the Store. * * @param recurse whether subfolders should be recursively deleted as well * @return true if the delete operation succeeds * @throws MessagingException if there was a problem accessing the store */ public abstract boolean delete(boolean recurse) throws MessagingException; /** * Rename this folder; the folder must be closed. * If the rename is successfull, a {@link FolderEvent#RENAMED} event is sent to * all listeners registered with this folder or with the store. * * @param newName the new name for this folder * @return true if the rename succeeded * @throws MessagingException if there was a problem accessing the store */ public abstract boolean renameTo(Folder newName) throws MessagingException; /** * Open this folder; the folder must be able to contain messages and * must currently be closed. If the folder is opened successfully then * a {@link ConnectionEvent#OPENED} event is sent to listeners registered * with this Folder. *

* Whether the Store allows multiple connections or if it allows multiple * writers is implementation defined. * * @param mode READ_ONLY or READ_WRITE * @throws MessagingException if there was a problem accessing the store */ public abstract void open(int mode) throws MessagingException; /** * Close this folder; it must already be open. * A {@link ConnectionEvent#CLOSED} event is sent to all listeners registered * with this folder. * * @param expunge whether to expunge all deleted messages * @throws MessagingException if there was a problem accessing the store; the folder is still closed */ public abstract void close(boolean expunge) throws MessagingException; /** * Indicates that the folder has been opened. * * @return true if the folder is open */ public abstract boolean isOpen(); /** * Return the mode of this folder ass passed to {@link #open(int)}, or -1 if * the folder is closed. * * @return the mode this folder was opened with */ public int getMode() { return mode; } /** * Get the flags supported by this folder. * * @return the flags supported by this folder, or null if unknown * @see Flags */ public abstract Flags getPermanentFlags(); /** * Return the number of messages this folder contains. * If this operation is invoked on a closed folder, the implementation * may choose to return -1 to avoid the expense of opening the folder. * * @return the number of messages, or -1 if unknown * @throws MessagingException if there was a problem accessing the store */ public abstract int getMessageCount() throws MessagingException; /** * Return the numbew of messages in this folder that have the {@link Flag.RECENT} flag set. * If this operation is invoked on a closed folder, the implementation * may choose to return -1 to avoid the expense of opening the folder. * The default implmentation of this method iterates over all messages * in the folder; subclasses should override if possible to provide a more * efficient implementation. * * @return the number of new messages, or -1 if unknown * @throws MessagingException if there was a problem accessing the store */ public int getNewMessageCount() throws MessagingException { return getCount(Flags.Flag.RECENT, true); } /** * Return the numbew of messages in this folder that do not have the {@link Flag.SEEN} flag set. * If this operation is invoked on a closed folder, the implementation * may choose to return -1 to avoid the expense of opening the folder. * The default implmentation of this method iterates over all messages * in the folder; subclasses should override if possible to provide a more * efficient implementation. * * @return the number of new messages, or -1 if unknown * @throws MessagingException if there was a problem accessing the store */ public int getUnreadMessageCount() throws MessagingException { return getCount(Flags.Flag.SEEN, false); } /** * Return the numbew of messages in this folder that have the {@link Flag.DELETED} flag set. * If this operation is invoked on a closed folder, the implementation * may choose to return -1 to avoid the expense of opening the folder. * The default implmentation of this method iterates over all messages * in the folder; subclasses should override if possible to provide a more * efficient implementation. * * @return the number of new messages, or -1 if unknown * @throws MessagingException if there was a problem accessing the store */ public int getDeletedMessageCount() throws MessagingException { return getCount(Flags.Flag.DELETED, true); } private int getCount(Flag flag, boolean value) throws MessagingException { if (!isOpen()) { return -1; } Message[] messages = getMessages(); int total = 0; for (int i = 0; i < messages.length; i++) { if (messages[i].getFlags().contains(flag) == value) { total++; } } return total; } /** * Retrieve the message with the specified index in this Folder; * messages indices start at 1 not zero. * Clients should note that the index for a specific message may change * if the folder is expunged; {@link Message} objects should be used as * references instead. * * @param index the index of the message to fetch * @return the message * @throws MessagingException if there was a problem accessing the store */ public abstract Message getMessage(int index) throws MessagingException; /** * Retrieve messages with index between start and end inclusive * * @param start index of first message * @param end index of last message * @return an array of messages from start to end inclusive * @throws MessagingException if there was a problem accessing the store */ public Message[] getMessages(int start, int end) throws MessagingException { Message[] result = new Message[end - start + 1]; for (int i = 0; i < result.length; i++) { result[i] = getMessage(start++); } return result; } /** * Retrieve messages with the specified indices. * * @param ids the indices of the messages to fetch * @return the specified messages * @throws MessagingException if there was a problem accessing the store */ public Message[] getMessages(int ids[]) throws MessagingException { Message[] result = new Message[ids.length]; for (int i = 0; i < ids.length; i++) { result[i] = getMessage(ids[i]); } return result; } /** * Retrieve all messages. * * @return all messages in this folder * @throws MessagingException if there was a problem accessing the store */ public Message[] getMessages() throws MessagingException { return getMessages(1, getMessageCount()); } /** * Append the supplied messages to this folder. A {@link MessageCountEvent} is sent * to all listeners registered with this folder when all messages have been appended. * If the array contains a previously expunged message, it must be re-appended to the Store * and implementations must not abort this operation. * * @param messages the messages to append * @throws MessagingException if there was a problem accessing the store */ public abstract void appendMessages(Message[] messages) throws MessagingException; /** * Hint to the store to prefetch information on the supplied messaged. * Subclasses should override this method to provide an efficient implementation; * the default implementation in this class simply returns. * * @param messages messages for which information should be fetched * @param profile the information to fetch * @throws MessagingException if there was a problem accessing the store * @see FetchProfile */ public void fetch(Message[] messages, FetchProfile profile) throws MessagingException { return; } /** * Set flags on the messages to the supplied value; all messages must belong to this folder. * This method may be overridden by subclasses that can optimize the setting * of flags on multiple messages at once; the default implementation simply calls * {@link Message#setFlags(Flags, boolean)} for each supplied messages. * * @param messages whose flags should be set * @param flags the set of flags to modify * @param value the value the flags should be set to * @throws MessagingException if there was a problem accessing the store */ public void setFlags(Message[] messages, Flags flags, boolean value) throws MessagingException { for (int i = 0; i < messages.length; i++) { Message message = messages[i]; message.setFlags(flags, value); } } /** * Set flags on a range of messages to the supplied value. * This method may be overridden by subclasses that can optimize the setting * of flags on multiple messages at once; the default implementation simply * gets each message and then calls {@link Message#setFlags(Flags, boolean)}. * * @param start first message end set * @param end last message end set * @param flags the set of flags end modify * @param value the value the flags should be set end * @throws MessagingException if there was a problem accessing the store */ public void setFlags(int start, int end, Flags flags, boolean value) throws MessagingException { for (int i = start; i <= end; i++) { Message message = getMessage(i); message.setFlags(flags, value); } } /** * Set flags on a set of messages to the supplied value. * This method may be overridden by subclasses that can optimize the setting * of flags on multiple messages at once; the default implementation simply * gets each message and then calls {@link Message#setFlags(Flags, boolean)}. * * @param ids the indexes of the messages to set * @param flags the set of flags end modify * @param value the value the flags should be set end * @throws MessagingException if there was a problem accessing the store */ public void setFlags(int ids[], Flags flags, boolean value) throws MessagingException { for (int i = 0; i < ids.length; i++) { Message message = getMessage(ids[i]); message.setFlags(flags, value); } } /** * Copy the specified messages to another folder. * The default implementation simply appends the supplied messages to the * target folder using {@link #appendMessages(Message[])}. * @param messages the messages to copy * @param folder the folder to copy to * @throws MessagingException if there was a problem accessing the store */ public void copyMessages(Message[] messages, Folder folder) throws MessagingException { folder.appendMessages(messages); } /** * Permanently delete all supplied messages that have the DELETED flag set from the Store. * The original message indices of all messages actually deleted are returned and a * {@link MessageCountEvent} event is sent to all listeners with this folder. The expunge * may cause the indices of all messaged that remain in the folder to change. * * @return the original indices of messages that were actually deleted * @throws MessagingException if there was a problem accessing the store */ public abstract Message[] expunge() throws MessagingException; /** * Search this folder for messages matching the supplied search criteria. * The default implementation simply invoke search(term, getMessages()) * applying the search over all messages in the folder; subclasses may provide * a more efficient mechanism. * * @param term the search criteria * @return an array containing messages that match the criteria * @throws MessagingException if there was a problem accessing the store */ public Message[] search(SearchTerm term) throws MessagingException { return search(term, getMessages()); } /** * Search the supplied messages for those that match the supplied criteria; * messages must belong to this folder. * The default implementation iterates through the messages, returning those * whose {@link Message#match(javax.mail.search.SearchTerm)} method returns true; * subclasses may provide a more efficient implementation. * * @param term the search criteria * @param messages the messages to search * @return an array containing messages that match the criteria * @throws MessagingException if there was a problem accessing the store */ public Message[] search(SearchTerm term, Message[] messages) throws MessagingException { List result = new ArrayList(messages.length); for (int i = 0; i < messages.length; i++) { Message message = messages[i]; if (message.match(term)) { result.add(message); } } return (Message[]) result.toArray(new Message[result.size()]); } public void addConnectionListener(ConnectionListener listener) { connectionListeners.add(listener); } public void removeConnectionListener(ConnectionListener listener) { connectionListeners.remove(listener); } protected void notifyConnectionListeners(int type) { queueEvent(new ConnectionEvent(this, type), connectionListeners); } public void addFolderListener(FolderListener listener) { folderListeners.add(listener); } public void removeFolderListener(FolderListener listener) { folderListeners.remove(listener); } protected void notifyFolderListeners(int type) { queueEvent(new FolderEvent(this, this, type), folderListeners); } protected void notifyFolderRenamedListeners(Folder newFolder) { queueEvent(new FolderEvent(this, this, newFolder, FolderEvent.RENAMED), folderListeners); } public void addMessageCountListener(MessageCountListener listener) { messageCountListeners.add(listener); } public void removeMessageCountListener(MessageCountListener listener) { messageCountListeners.remove(listener); } protected void notifyMessageAddedListeners(Message[] messages) { queueEvent(new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages), messageChangedListeners); } protected void notifyMessageRemovedListeners(boolean removed, Message[] messages) { queueEvent(new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages), messageChangedListeners); } public void addMessageChangedListener(MessageChangedListener listener) { messageChangedListeners.add(listener); } public void removeMessageChangedListener(MessageChangedListener listener) { messageChangedListeners.remove(listener); } protected void notifyMessageChangedListeners(int type, Message message) { queueEvent(new MessageChangedEvent(this, type, message), messageChangedListeners); } /** * Unregisters all listeners. */ protected void finalize() throws Throwable { // shut our queue down, if needed. if (queue != null) { queue.stop(); queue = null; } connectionListeners.clear(); folderListeners.clear(); messageChangedListeners.clear(); messageCountListeners.clear(); store = null; super.finalize(); } /** * Returns the full name of this folder; if null, returns the value from the superclass. * @return a string form of this folder */ public String toString() { String name = getFullName(); return name == null ? super.toString() : name; } /** * Add an event on the event queue, creating the queue if this is the * first event with actual listeners. * * @param event The event to dispatch. * @param listeners The listener list. */ private synchronized void queueEvent(MailEvent event, ArrayList listeners) { // if there are no listeners to dispatch this to, don't put it on the queue. // This allows us to delay creating the queue (and its new thread) until // we if (listeners.isEmpty()) { return; } // first real event? Time to get the queue kicked off. if (queue == null) { queue = new EventQueue(); } // tee it up and let it rip. queue.queueEvent(event, (List)listeners.clone()); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MultipartDataSource.java0000664000175000017500000000215310517560657026626 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import javax.activation.DataSource; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface MultipartDataSource extends DataSource { public abstract BodyPart getBodyPart(int index) throws MessagingException; public abstract int getCount(); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/FetchProfile.java0000664000175000017500000001013110702421230025212 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.ArrayList; import java.util.List; /** * A FetchProfile defines a list of message attributes that a client wishes to prefetch * from the server during a fetch operation. * * Clients can either specify individual headers, or can reference common profiles * as defined by {@link FetchProfile.Item FetchProfile.Item}. * * @version $Rev: 582797 $ $Date: 2007-10-08 08:29:12 -0400 (Mon, 08 Oct 2007) $ */ public class FetchProfile { /** * Inner class that defines sets of headers that are commonly bundled together * in a FetchProfile. */ public static class Item { /** * Item for fetching information about the content of the message. * * This includes all the headers about the content including but not limited to: * Content-Type, Content-Disposition, Content-Description, Size and Line-Count */ public static final Item CONTENT_INFO = new Item("CONTENT_INFO"); /** * Item for fetching information about the envelope of the message. * * This includes all the headers comprising the envelope including but not limited to: * From, To, Cc, Bcc, Reply-To, Subject and Date * * For IMAP4, this should also include the ENVELOPE data item. * */ public static final Item ENVELOPE = new Item("ENVELOPE"); /** * Item for fetching information about message flags. * Generall corresponds to the X-Flags header. */ public static final Item FLAGS = new Item("FLAGS"); protected Item(String name) { // hmmm, name is passed in but we are not allowed to provide accessors // or to override equals/hashCode so what use is it? } } // use Lists as we don't expect contains to be called often and the number of elements should be small private final List items = new ArrayList(); private final List headers = new ArrayList(); /** * Add a predefined profile of headers. * * @param item the profile to add */ public void add(Item item) { items.add(item); } /** * Add a specific header. * @param header the header whose value should be prefetched */ public void add(String header) { headers.add(header); } /** * Determine if the given profile item is already included. * @param item the profile to check for * @return true if the profile item is already included */ public boolean contains(Item item) { return items.contains(item); } /** * Determine if the specified header is already included. * @param header the header to check for * @return true if the header is already included */ public boolean contains(String header) { return headers.contains(header); } /** * Get the profile items already included. * @return the items already added to this profile */ public Item[] getItems() { return (Item[]) items.toArray(new Item[items.size()]); } /** Get the headers that have already been included. * @return the headers already added to this profile */ public String[] getHeaderNames() { return (String[]) headers.toArray(new String[headers.size()]); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Store.java0000664000175000017500000001242710675734274023776 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.Vector; import javax.mail.event.FolderEvent; import javax.mail.event.FolderListener; import javax.mail.event.StoreEvent; import javax.mail.event.StoreListener; /** * Abstract class that represents a message store. * * @version $Rev: 578802 $ $Date: 2007-09-24 09:16:44 -0400 (Mon, 24 Sep 2007) $ */ public abstract class Store extends Service { private static final Folder[] FOLDER_ARRAY = new Folder[0]; private final Vector folderListeners = new Vector(2); private final Vector storeListeners = new Vector(2); /** * Constructor specifying session and url of this store. * Subclasses MUST provide a constructor with this signature. * * @param session the session associated with this store * @param name the URL of the store */ protected Store(Session session, URLName name) { super(session, name); } /** * Return a Folder object that represents the root of the namespace for the current user. * * Note that in some store configurations (such as IMAP4) the root folder might * not be the INBOX folder. * * @return the root Folder * @throws MessagingException if there was a problem accessing the store */ public abstract Folder getDefaultFolder() throws MessagingException; /** * Return the Folder corresponding to the given name. * The folder might not physically exist; the {@link Folder#exists()} method can be used * to determine if it is real. * @param name the name of the Folder to return * @return the corresponding folder * @throws MessagingException if there was a problem accessing the store */ public abstract Folder getFolder(String name) throws MessagingException; /** * Return the folder identified by the URLName; the URLName must refer to this Store. * Implementations may use the {@link URLName#getFile()} method to determined the folder name. * * @param name the folder to return * @return the corresponding folder * @throws MessagingException if there was a problem accessing the store */ public abstract Folder getFolder(URLName name) throws MessagingException; /** * Return the root folders of the personal namespace belonging to the current user. * * The default implementation simply returns an array containing the folder returned by {@link #getDefaultFolder()}. * @return the root folders of the user's peronal namespaces * @throws MessagingException if there was a problem accessing the store */ public Folder[] getPersonalNamespaces() throws MessagingException { return new Folder[]{getDefaultFolder()}; } /** * Return the root folders of the personal namespaces belonging to the supplied user. * * The default implementation simply returns an empty array. * * @param user the user whose namespaces should be returned * @return the root folders of the given user's peronal namespaces * @throws MessagingException if there was a problem accessing the store */ public Folder[] getUserNamespaces(String user) throws MessagingException { return FOLDER_ARRAY; } /** * Return the root folders of namespaces that are intended to be shared between users. * * The default implementation simply returns an empty array. * @return the root folders of all shared namespaces * @throws MessagingException if there was a problem accessing the store */ public Folder[] getSharedNamespaces() throws MessagingException { return FOLDER_ARRAY; } public void addStoreListener(StoreListener listener) { storeListeners.add(listener); } public void removeStoreListener(StoreListener listener) { storeListeners.remove(listener); } protected void notifyStoreListeners(int type, String message) { queueEvent(new StoreEvent(this, type, message), storeListeners); } public void addFolderListener(FolderListener listener) { folderListeners.add(listener); } public void removeFolderListener(FolderListener listener) { folderListeners.remove(listener); } protected void notifyFolderListeners(int type, Folder folder) { queueEvent(new FolderEvent(this, folder, type), folderListeners); } protected void notifyFolderRenamedListeners(Folder oldFolder, Folder newFolder) { queueEvent(new FolderEvent(this, oldFolder, newFolder, FolderEvent.RENAMED), folderListeners); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Provider.java0000664000175000017500000000461410517560657024470 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class Provider { /** * A enumeration inner class that defines Provider types. */ public static class Type { /** * A message store provider such as POP3 or IMAP4. */ public static final Type STORE = new Type(); /** * A message transport provider such as SMTP. */ public static final Type TRANSPORT = new Type(); private Type() { } } private final String className; private final String protocol; private final Type type; private final String vendor; private final String version; public Provider(Type type, String protocol, String className, String vendor, String version) { this.protocol = protocol; this.className = className; this.type = type; this.vendor = vendor; this.version = version; } public String getClassName() { return className; } public String getProtocol() { return protocol; } public Type getType() { return type; } public String getVendor() { return vendor; } public String getVersion() { return version; } public String toString() { return "protocol=" + protocol + "; type=" + type + "; class=" + className + (vendor == null ? "" : "; vendor=" + vendor) + (version == null ? "" : ";version=" + version); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/BodyPart.java0000664000175000017500000000233010517560657024413 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class BodyPart implements Part { protected Multipart parent; public Multipart getParent() { return parent; } // Can't be public. Not strictly required for spec, but mirrors Sun's javamail api impl. void setParent(Multipart parent) { this.parent = parent; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/StoreClosedException.java0000664000175000017500000000247511345370365027001 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class StoreClosedException extends MessagingException { private static final long serialVersionUID = -3145392336120082655L; private transient Store _store; public StoreClosedException(Store store) { super(); _store = store; } public StoreClosedException(Store store, String message) { super(message); } public Store getStore() { return _store; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/AuthenticationFailedException.java0000664000175000017500000000230711345370365030631 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class AuthenticationFailedException extends MessagingException { private static final long serialVersionUID = 492080754054436511L; public AuthenticationFailedException() { super(); } public AuthenticationFailedException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/IllegalWriteException.java0000664000175000017500000000225611345370365027134 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class IllegalWriteException extends MessagingException { private static final long serialVersionUID = 3974370223328268013L; public IllegalWriteException() { super(); } public IllegalWriteException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Quota.java0000664000175000017500000000735310675734274023775 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * A representation of a Quota item for a given quota root. * * @version $Rev: 578802 $ $Date: 2007-09-24 09:16:44 -0400 (Mon, 24 Sep 2007) $ */ public class Quota { /** * The name of the quota root. */ public String quotaRoot; /** * The resources associated with this quota root. */ public Resource[] resources; /** * Create a Quota with the given name and no resources. * * @param quotaRoot The quota root name. */ public Quota(String quotaRoot) { this.quotaRoot = quotaRoot; } /** * Set a limit value for a resource. If the resource is not * currently associated with this Quota, a new Resource item is * added to the resources list. * * @param name The target resource name. * @param limit The new limit value for the resource. */ public void setResourceLimit(String name, long limit) { Resource target = findResource(name); target.limit = limit; } /** * Locate a particular named resource, adding one to the list * if it does not exist. * * @param name The target resource name. * * @return A Resource item for this named resource (either existing or new). */ private Resource findResource(String name) { // no resources yet? Make it so. if (resources == null) { Resource target = new Resource(name, 0, 0); resources = new Resource[] { target }; return target; } // see if this one exists and return it. for (int i = 0; i < resources.length; i++) { Resource current = resources[i]; if (current.name.equalsIgnoreCase(name)) { return current; } } // have to extend the array...this is a pain. Resource[] newResources = new Resource[resources.length + 1]; System.arraycopy(resources, 0, newResources, 0, resources.length); Resource target = new Resource(name, 0, 0); newResources[resources.length] = target; resources = newResources; return target; } /** * A representation of a given resource definition. */ public static class Resource { /** * The resource name. */ public String name; /** * The current resource usage. */ public long usage; /** * The limit value for this resource. */ public long limit; /** * Construct a Resource object from the given name and usage/limit * information. * * @param name The Resource name. * @param usage The current resource usage. * @param limit The Resource limit value. */ public Resource(String name, long usage, long limit) { this.name = name; this.usage = usage; this.limit = limit; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/MessageRemovedException.java0000664000175000017500000000226411345370365027455 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MessageRemovedException extends MessagingException { private static final long serialVersionUID = 1951292550679528690L; public MessageRemovedException() { super(); } public MessageRemovedException(String message) { super(message); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/0000775000175000017500000000000011703375730023141 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/ConnectionEvent.java0000664000175000017500000000374511345370365027117 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class ConnectionEvent extends MailEvent { private static final long serialVersionUID = -1855480171284792957L; /** * A connection was opened. */ public static final int OPENED = 1; /** * A connection was disconnected. */ public static final int DISCONNECTED = 2; /** * A connection was closed. */ public static final int CLOSED = 3; protected int type; public ConnectionEvent(Object source, int type) { super(source); this.type = type; } public int getType() { return type; } public void dispatch(Object listener) { // assume that it is the right listener type ConnectionListener l = (ConnectionListener) listener; switch (type) { case OPENED: l.opened(this); break; case DISCONNECTED: l.disconnected(this); break; case CLOSED: l.closed(this); break; default: throw new IllegalArgumentException("Invalid type " + type); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/ConnectionListener.java0000664000175000017500000000262110517560657027620 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * Listener for handling connection events. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface ConnectionListener extends EventListener { /** * Called when a connection is opened. */ public abstract void opened(ConnectionEvent event); /** * Called when a connection is disconnected. */ public abstract void disconnected(ConnectionEvent event); /** * Called when a connection is closed. */ public abstract void closed(ConnectionEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/TransportAdapter.java0000664000175000017500000000245210517560657027312 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; /** * An adaptor that receives transport events. * This is a default implementation where the handlers perform no action. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class TransportAdapter implements TransportListener { public void messageDelivered(TransportEvent event) { } public void messageNotDelivered(TransportEvent event) { } public void messagePartiallyDelivered(TransportEvent event) { } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/TransportListener.java0000664000175000017500000000231310517560657027513 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface TransportListener extends EventListener { public abstract void messageDelivered(TransportEvent event); public abstract void messageNotDelivered(TransportEvent event); public abstract void messagePartiallyDelivered(TransportEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MessageCountListener.java0000664000175000017500000000220210517560657030111 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface MessageCountListener extends EventListener { public abstract void messagesAdded(MessageCountEvent event); public abstract void messagesRemoved(MessageCountEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MessageCountEvent.java0000664000175000017500000000612711345370365027412 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Folder; import javax.mail.Message; /** * Event indicating a change in the number of messages in a folder. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MessageCountEvent extends MailEvent { private static final long serialVersionUID = -7447022340837897369L; /** * Messages were added to the folder. */ public static final int ADDED = 1; /** * Messages were removed from the folder. */ public static final int REMOVED = 2; /** * The affected messages. */ protected transient Message msgs[]; /** * The event type. */ protected int type; /** * If true, then messages were expunged from the folder by this client * and message numbers reflect the deletion; if false, then the change * was the result of an expunge by a different client. */ protected boolean removed; /** * Construct a new event. * * @param folder the folder containing the messages * @param type the event type * @param removed indicator of whether messages were expunged by this client * @param messages the affected messages */ public MessageCountEvent(Folder folder, int type, boolean removed, Message messages[]) { super(folder); this.msgs = messages; this.type = type; this.removed = removed; } /** * Return the event type. * * @return the event type */ public int getType() { return type; } /** * @return whether this event was the result of an expunge by this client * @see MessageCountEvent#removed */ public boolean isRemoved() { return removed; } /** * Return the affected messages. * * @return the affected messages */ public Message[] getMessages() { return msgs; } public void dispatch(Object listener) { MessageCountListener l = (MessageCountListener) listener; switch (type) { case ADDED: l.messagesAdded(this); break; case REMOVED: l.messagesRemoved(this); break; default: throw new IllegalArgumentException("Invalid type " + type); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MessageCountAdapter.java0000664000175000017500000000235210517560657027712 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; /** * An adaptor that receives message count events. * This is a default implementation where the handlers perform no action. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class MessageCountAdapter implements MessageCountListener { public void messagesAdded(MessageCountEvent event) { } public void messagesRemoved(MessageCountEvent event) { } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/StoreEvent.java0000664000175000017500000000440711345370365026110 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Store; /** * Event representing motifications from the Store connection. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class StoreEvent extends MailEvent { private static final long serialVersionUID = 1938704919992515330L; /** * Indicates that this message is an alert. */ public static final int ALERT = 1; /** * Indicates that this message is a notice. */ public static final int NOTICE = 2; /** * The message type. */ protected int type; /** * The text to be presented to the user. */ protected String message; /** * Construct a new event. * * @param store the Store that initiated the notification * @param type the message type * @param message the text to be presented to the user */ public StoreEvent(Store store, int type, String message) { super(store); this.type = type; this.message = message; } /** * Return the message type. * * @return the message type */ public int getMessageType() { return type; } /** * Return the text to be displayed to the user. * * @return the text to be displayed to the user */ public String getMessage() { return message; } public void dispatch(Object listener) { ((StoreListener) listener).notification(this); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MailEvent.java0000664000175000017500000000233411345370365025673 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventObject; /** * Common base class for mail events. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public abstract class MailEvent extends EventObject { private static final long serialVersionUID = 1846275636325456631L; public MailEvent(Object source) { super(source); } public abstract void dispatch(Object listener); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/FolderAdapter.java0000664000175000017500000000240710517560657026531 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; /** * An adaptor that receives connection events. * This is a default implementation where the handlers perform no action. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class FolderAdapter implements FolderListener { public void folderCreated(FolderEvent event) { } public void folderDeleted(FolderEvent event) { } public void folderRenamed(FolderEvent event) { } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/StoreListener.java0000664000175000017500000000205710517560657026620 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface StoreListener extends EventListener { public abstract void notification(StoreEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MessageChangedEvent.java0000664000175000017500000000411411345370365027645 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Message; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class MessageChangedEvent extends MailEvent { private static final long serialVersionUID = -4974972972105535108L; /** * The message's flags changed. */ public static final int FLAGS_CHANGED = 1; /** * The messages envelope changed. */ public static final int ENVELOPE_CHANGED = 2; protected transient Message msg; protected int type; /** * Constructor. * * @param source the folder that owns the message * @param type the event type * @param message the affected message */ public MessageChangedEvent(Object source, int type, Message message) { super(source); msg = message; this.type = type; } public void dispatch(Object listener) { MessageChangedListener l = (MessageChangedListener) listener; l.messageChanged(this); } /** * Return the affected message. * @return the affected message */ public Message getMessage() { return msg; } /** * Return the type of change. * @return the event type */ public int getMessageChangeType() { return type; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/ConnectionAdapter.java0000664000175000017500000000241410517560657027413 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; /** * An adaptor that receives connection events. * This is a default implementation where the handlers perform no action. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public abstract class ConnectionAdapter implements ConnectionListener { public void closed(ConnectionEvent event) { } public void disconnected(ConnectionEvent event) { } public void opened(ConnectionEvent event) { } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/FolderListener.java0000664000175000017500000000225210517560657026734 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface FolderListener extends EventListener { public abstract void folderCreated(FolderEvent event); public abstract void folderDeleted(FolderEvent event); public abstract void folderRenamed(FolderEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/MessageChangedListener.java0000664000175000017500000000210310517560657030352 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import java.util.EventListener; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public interface MessageChangedListener extends EventListener { public abstract void messageChanged(MessageChangedEvent event); } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/FolderEvent.java0000664000175000017500000000562011345370365026225 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Folder; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class FolderEvent extends MailEvent { private static final long serialVersionUID = 5278131310563694307L; public static final int CREATED = 1; public static final int DELETED = 2; public static final int RENAMED = 3; protected transient Folder folder; protected transient Folder newFolder; protected int type; /** * Constructor used for RENAMED events. * * @param source the source of the event * @param oldFolder the folder that was renamed * @param newFolder the folder with the new name * @param type the event type */ public FolderEvent(Object source, Folder oldFolder, Folder newFolder, int type) { super(source); folder = oldFolder; this.newFolder = newFolder; this.type = type; } /** * Constructor other events. * * @param source the source of the event * @param folder the folder affected * @param type the event type */ public FolderEvent(Object source, Folder folder, int type) { this(source, folder, null, type); } public void dispatch(Object listener) { FolderListener l = (FolderListener) listener; switch (type) { case CREATED: l.folderCreated(this); break; case DELETED: l.folderDeleted(this); break; case RENAMED: l.folderRenamed(this); break; default: throw new IllegalArgumentException("Invalid type " + type); } } /** * Return the affected folder. * @return the affected folder */ public Folder getFolder() { return folder; } /** * Return the new folder; only applicable to RENAMED events. * @return the new folder */ public Folder getNewFolder() { return newFolder; } /** * Return the event type. * @return the event type */ public int getType() { return type; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/event/TransportEvent.java0000664000175000017500000000727211345370365027013 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Address; import javax.mail.Message; import javax.mail.Transport; /** * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class TransportEvent extends MailEvent { private static final long serialVersionUID = -4729852364684273073L; /** * Indicates that the message has successfully been delivered to all * recipients. */ public static final int MESSAGE_DELIVERED = 1; /** * Indicates that no messages could be delivered. */ public static final int MESSAGE_NOT_DELIVERED = 2; /** * Indicates that some of the messages were successfully delivered * but that some failed. */ public static final int MESSAGE_PARTIALLY_DELIVERED = 3; /** * The event type. */ protected int type; /** * Addresses to which the message was successfully delivered. */ protected transient Address[] validSent; /** * Addresses which are valid but to which the message was not sent. */ protected transient Address[] validUnsent; /** * Addresses that are invalid. */ protected transient Address[] invalid; /** * The message associated with this event. */ protected transient Message msg; /** * Construct a new event, * * @param transport the transport attempting to deliver the message * @param type the event type * @param validSent addresses to which the message was successfully delivered * @param validUnsent addresses which are valid but to which the message was not sent * @param invalid invalid addresses * @param message the associated message */ public TransportEvent(Transport transport, int type, Address[] validSent, Address[] validUnsent, Address[] invalid, Message message) { super(transport); this.type = type; this.validSent = validSent; this.validUnsent = validUnsent; this.invalid = invalid; this.msg = message; } public Address[] getValidSentAddresses() { return validSent; } public Address[] getValidUnsentAddresses() { return validUnsent; } public Address[] getInvalidAddresses() { return invalid; } public Message getMessage() { return msg; } public int getType() { return type; } public void dispatch(Object listener) { TransportListener l = (TransportListener) listener; switch (type) { case MESSAGE_DELIVERED: l.messageDelivered(this); break; case MESSAGE_NOT_DELIVERED: l.messageNotDelivered(this); break; case MESSAGE_PARTIALLY_DELIVERED: l.messagePartiallyDelivered(this); break; default: throw new IllegalArgumentException("Invalid type " + type); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/Flags.java0000664000175000017500000002212511345370365023722 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.Serializable; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * Representation of flags that may be associated with a message. * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class, * or user-defined flags defined by a String. The system flags represent those expected * to be provided by most folder systems; user-defined flags allow for additional flags * on a per-provider basis. *

* This class is Serializable but compatibility is not guaranteed across releases. * * @version $Rev: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $ */ public class Flags implements Cloneable, Serializable { private static final long serialVersionUID = 6243590407214169028L; public static final class Flag { /** * Flag that indicates that the message has been replied to; has a bit value of 1. */ public static final Flag ANSWERED = new Flag(1); /** * Flag that indicates that the message has been marked for deletion and * should be removed on a subsequent expunge operation; has a bit value of 2. */ public static final Flag DELETED = new Flag(2); /** * Flag that indicates that the message is a draft; has a bit value of 4. */ public static final Flag DRAFT = new Flag(4); /** * Flag that indicates that the message has been flagged; has a bit value of 8. */ public static final Flag FLAGGED = new Flag(8); /** * Flag that indicates that the message has been delivered since the last time * this folder was opened; has a bit value of 16. */ public static final Flag RECENT = new Flag(16); /** * Flag that indicates that the message has been viewed; has a bit value of 32. * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()} * methods. */ public static final Flag SEEN = new Flag(32); /** * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000. */ public static final Flag USER = new Flag(0x80000000); private final int mask; private Flag(int mask) { this.mask = mask; } } // the Serialized form of this class required the following two fields to be persisted // this leads to a specific type of implementation private int system_flags; private final Hashtable user_flags; /** * Construct a Flags instance with no flags set. */ public Flags() { user_flags = new Hashtable(); } /** * Construct a Flags instance with a supplied system flag set. * @param flag the system flag to set */ public Flags(Flag flag) { system_flags = flag.mask; user_flags = new Hashtable(); } /** * Construct a Flags instance with a same flags set. * @param flags the instance to copy */ public Flags(Flags flags) { system_flags = flags.system_flags; user_flags = new Hashtable(flags.user_flags); } /** * Construct a Flags instance with the supplied user flags set. * Question: should this automatically set the USER system flag? * @param name the user flag to set */ public Flags(String name) { user_flags = new Hashtable(); user_flags.put(name.toLowerCase(), name); } /** * Set a system flag. * @param flag the system flag to set */ public void add(Flag flag) { system_flags |= flag.mask; } /** * Set all system and user flags from the supplied Flags. * Question: do we need to check compatibility of USER flags? * @param flags the Flags to add */ public void add(Flags flags) { system_flags |= flags.system_flags; user_flags.putAll(flags.user_flags); } /** * Set a user flag. * Question: should this fail if the USER system flag is not set? * @param name the user flag to set */ public void add(String name) { user_flags.put(name.toLowerCase(), name); } /** * Return a copy of this instance. * @return a copy of this instance */ public Object clone() { return new Flags(this); } /** * See if the supplied system flags are set * @param flag the system flags to check for * @return true if the flags are set */ public boolean contains(Flag flag) { return (system_flags & flag.mask) != 0; } /** * See if all of the supplied Flags are set * @param flags the flags to check for * @return true if all the supplied system and user flags are set */ public boolean contains(Flags flags) { return ((system_flags & flags.system_flags) == flags.system_flags) && user_flags.keySet().containsAll(flags.user_flags.keySet()); } /** * See if the supplied user flag is set * @param name the user flag to check for * @return true if the flag is set */ public boolean contains(String name) { return user_flags.containsKey(name.toLowerCase()); } /** * Equality is defined as true if the other object is a instanceof Flags with the * same system and user flags set (using a case-insensitive name comparison for user flags). * @param other the instance to compare against * @return true if the two instance are the same */ public boolean equals(Object other) { if (other == this) return true; if (other instanceof Flags == false) return false; final Flags flags = (Flags) other; return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet()); } /** * Calculate a hashCode for this instance * @return a hashCode for this instance */ public int hashCode() { return system_flags ^ user_flags.keySet().hashCode(); } /** * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set * @return the system flags that have been set */ public Flag[] getSystemFlags() { // assumption: it is quicker to calculate the size than it is to reallocate the array int size = 0; if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1; if ((system_flags & Flag.DELETED.mask) != 0) size += 1; if ((system_flags & Flag.DRAFT.mask) != 0) size += 1; if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1; if ((system_flags & Flag.RECENT.mask) != 0) size += 1; if ((system_flags & Flag.SEEN.mask) != 0) size += 1; if ((system_flags & Flag.USER.mask) != 0) size += 1; Flag[] result = new Flag[size]; if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER; if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN; if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT; if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED; if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT; if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED; if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED; return result; } /** * Return a list of user flags that have been set * @return a list of user flags */ public String[] getUserFlags() { return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]); } /** * Unset the supplied system flag. * Question: what happens if we unset the USER flags and user flags are set? * @param flag the flag to clear */ public void remove(Flag flag) { system_flags &= ~flag.mask; } /** * Unset all flags from the supplied instance. * @param flags the flags to clear */ public void remove(Flags flags) { system_flags &= ~flags.system_flags; user_flags.keySet().removeAll(flags.user_flags.keySet()); } /** * Unset the supplied user flag. * @param name the flag to clear */ public void remove(String name) { user_flags.remove(name.toLowerCase()); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/PasswordAuthentication.java0000664000175000017500000000253110517560657027374 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; /** * A data holder used by Authenticator to contain a username and password. * * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public final class PasswordAuthentication { private final String user; private final String password; public PasswordAuthentication(String user, String password) { this.user = user; this.password = password; } public String getUserName() { return user; } public String getPassword() { return password; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/javax/mail/URLName.java0000664000175000017500000002315310714667425024140 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.ByteArrayOutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; /** * @version $Rev: 593290 $ $Date: 2007-11-08 15:18:29 -0500 (Thu, 08 Nov 2007) $ */ public class URLName { private static final String nonEncodedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.*"; private String file; private String host; private String password; private int port; private String protocol; private String ref; private String username; protected String fullURL; private int hashCode; public URLName(String url) { parseString(url); } protected void parseString(String url) { URI uri; try { if (url == null) { uri = null; } else { uri = new URI(url); } } catch (URISyntaxException e) { uri = null; } if (uri == null) { protocol = null; host = null; port = -1; file = null; ref = null; username = null; password = null; return; } protocol = checkBlank(uri.getScheme()); host = checkBlank(uri.getHost()); port = uri.getPort(); file = checkBlank(uri.getPath()); // if the file starts with "/", we need to strip that off. // URL and URLName do not have the same behavior when it comes // to keeping that there. if (file != null && file.length() > 1 && file.startsWith("/")) { file = checkBlank(file.substring(1)); } ref = checkBlank(uri.getFragment()); String userInfo = checkBlank(uri.getUserInfo()); if (userInfo == null) { username = null; password = null; } else { int pos = userInfo.indexOf(':'); if (pos == -1) { username = userInfo; password = null; } else { username = userInfo.substring(0, pos); password = userInfo.substring(pos + 1); } } updateFullURL(); } public URLName(String protocol, String host, int port, String file, String username, String password) { this.protocol = checkBlank(protocol); this.host = checkBlank(host); this.port = port; if (file == null || file.length() == 0) { this.file = null; ref = null; } else { int pos = file.indexOf('#'); if (pos == -1) { this.file = file; ref = null; } else { this.file = file.substring(0, pos); ref = file.substring(pos + 1); } } this.username = checkBlank(username); if (this.username != null) { this.password = checkBlank(password); } else { this.password = null; } username = encode(username); password = encode(password); updateFullURL(); } public URLName(URL url) { protocol = checkBlank(url.getProtocol()); host = checkBlank(url.getHost()); port = url.getPort(); file = checkBlank(url.getFile()); ref = checkBlank(url.getRef()); String userInfo = checkBlank(url.getUserInfo()); if (userInfo == null) { username = null; password = null; } else { int pos = userInfo.indexOf(':'); if (pos == -1) { username = userInfo; password = null; } else { username = userInfo.substring(0, pos); password = userInfo.substring(pos + 1); } } updateFullURL(); } private static String checkBlank(String target) { if (target == null || target.length() == 0) { return null; } else { return target; } } private void updateFullURL() { hashCode = 0; StringBuffer buf = new StringBuffer(100); if (protocol != null) { buf.append(protocol).append(':'); if (host != null) { buf.append("//"); if (username != null) { buf.append(encode(username)); if (password != null) { buf.append(':').append(encode(password)); } buf.append('@'); } buf.append(host); if (port != -1) { buf.append(':').append(port); } if (file != null) { buf.append('/').append(file); } hashCode = buf.toString().hashCode(); if (ref != null) { buf.append('#').append(ref); } } } fullURL = buf.toString(); } public boolean equals(Object o) { if (o instanceof URLName == false) { return false; } URLName other = (URLName) o; // check same protocol - false if either is null if (protocol == null || other.protocol == null || !protocol.equals(other.protocol)) { return false; } if (port != other.port) { return false; } // check host - false if not (both null or both equal) return areSame(host, other.host) && areSame(file, other.file) && areSame(username, other.username) && areSame(password, other.password); } private static boolean areSame(String s1, String s2) { if (s1 == null) { return s2 == null; } else { return s1.equals(s2); } } public int hashCode() { return hashCode; } public String toString() { return fullURL; } public String getFile() { return file; } public String getHost() { return host; } public String getPassword() { return password; } public int getPort() { return port; } public String getProtocol() { return protocol; } public String getRef() { return ref; } public URL getURL() throws MalformedURLException { return new URL(fullURL); } public String getUsername() { return username; } /** * Perform an HTTP encoding to the username and * password elements of the URLName. * * @param v The input (uncoded) string. * * @return The HTTP encoded version of the string. */ private static String encode(String v) { // make sure we don't operate on a null string if (v == null) { return null; } boolean needsEncoding = false; for (int i = 0; i < v.length(); i++) { // not in the list of things that don't need encoding? if (nonEncodedChars.indexOf(v.charAt(i)) == -1) { // got to do this the hard way needsEncoding = true; break; } } // just fine the way it is. if (!needsEncoding) { return v; } // we know we're going to be larger, but not sure by how much. // just give a little extra StringBuffer encoded = new StringBuffer(v.length() + 10); // we get the bytes so that we can have the default encoding applied to // this string. This will flag the ones we need to give special processing to. byte[] data = v.getBytes(); for (int i = 0; i < data.length; i++) { // pick this up as a one-byte character The 7-bit ascii ones will be fine // here. char ch = (char)(data[i] & 0xff); // blanks get special treatment if (ch == ' ') { encoded.append('+'); } // not in the list of things that don't need encoding? else if (nonEncodedChars.indexOf(ch) == -1) { // forDigit() uses the lowercase letters for the radix. The HTML specifications // require the uppercase letters. char firstChar = Character.toUpperCase(Character.forDigit((ch >> 4) & 0xf, 16)); char secondChar = Character.toUpperCase(Character.forDigit(ch & 0xf, 16)); // now append the encoded triplet. encoded.append('%'); encoded.append(firstChar); encoded.append(secondChar); } else { // just add this one to the buffer encoded.append(ch); } } // convert to string form. return encoded.toString(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/0000775000175000017500000000000011703375727020562 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/0000775000175000017500000000000011703375727022003 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/0000775000175000017500000000000011703375727023622 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/0000775000175000017500000000000011703375727024544 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/MailProviderRegistry.java0000664000175000017500000000734011345242263031527 0ustar brianbrian/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogService; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * The activator that starts and manages the tracking of * JAF activation command maps */ public class MailProviderRegistry { // a list of all active mail provider config files static ConcurrentMap providers = new ConcurrentHashMap(); // a list of all active default provider config files static ConcurrentMap defaultProviders = new ConcurrentHashMap(); /** * Perform the check for an existing mailcap file when * a bundle is registered. * * @param bundle The potential provider bundle. * * @return A URL object if this bundle contains a mailcap file. */ static Object registerBundle(Bundle bundle) { // potential tracker return result Object result = null; // a given bundle might have a javamail.providers definition and/or a // default providers definition. URL url = bundle.getResource("META-INF/javamail.providers"); if (url != null) { providers.put(bundle.getBundleId(), url); // this indicates our interest result = url; } url = bundle.getResource("META-INF/javamail.default.providers"); if (url != null) { defaultProviders.put(bundle.getBundleId(), url); // this indicates our interest result = url; } // the url marks our interest in additional activity for this // bundle. return result; } /** * Remove a bundle from our potential mailcap pool. * * @param bundle The potential source bundle. */ static void unregisterBundle(Bundle bundle) { // remove these items providers.remove(bundle.getBundleId()); defaultProviders.remove(bundle.getBundleId()); } /** * Retrieve any located provider definitions * from bundles. * * @return A collection of the provider definition file * URLs. */ public static Collection getProviders() { return providers.values(); } /** * Retrieve any located default provider definitions * from bundles. * * @return A collection of the default provider definition file * URLs. */ public static Collection getDefaultProviders() { return defaultProviders.values(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/Activator.java0000664000175000017500000000677611345242263027351 0ustar brianbrian/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogService; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * The activator that starts and manages the tracking of * JAF activation command maps */ public class Activator extends org.apache.geronimo.osgi.locator.Activator { // tracker to watch for bundle updates protected BundleTracker bt; // service tracker for a logging service protected ServiceTracker lst; // an array of all active logging services. protected List logServices = new ArrayList(); @Override public synchronized void start(final BundleContext context) throws Exception { super.start(context); lst = new LogServiceTracker(context, LogService.class.getName(), null); lst.open(); bt = new BundleTracker(context, Bundle.ACTIVE, new MailProviderBundleTrackerCustomizer(this, context.getBundle())); bt.open(); } @Override public synchronized void stop(BundleContext context) throws Exception { bt.close(); lst.close(); super.stop(context); } void log(int level, String message) { synchronized (logServices) { for (LogService log : logServices) { log.log(level, message); } } } void log(int level, String message, Throwable th) { synchronized (logServices) { for (LogService log : logServices) { log.log(level, message, th); } } } private final class LogServiceTracker extends ServiceTracker { private LogServiceTracker(BundleContext context, String clazz, ServiceTrackerCustomizer customizer) { super(context, clazz, customizer); } @Override public Object addingService(ServiceReference reference) { Object svc = super.addingService(reference); if (svc instanceof LogService) { synchronized (logServices) { logServices.add((LogService) svc); } } return svc; } @Override public void removedService(ServiceReference reference, Object service) { synchronized (logServices) { logServices.remove(service); } super.removedService(reference, service); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/0000775000175000017500000000000011703375730025513 5ustar brianbrian././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableDecoderStream.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableDecoderS0000664000175000017500000000644211013271067032147 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; /** * An implementation of a FilterOutputStream that decodes the * stream data in Q-P encoding format. This version does the * decoding "on the fly" rather than decoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class QuotedPrintableDecoderStream extends FilterInputStream { // our decoder for processing the data protected QuotedPrintableEncoder decoder; /** * Stream constructor. * * @param in The InputStream this stream is filtering. */ public QuotedPrintableDecoderStream(InputStream in) { super(in); decoder = new QuotedPrintableEncoder(); } // in order to function as a filter, these streams need to override the different // read() signatures. /** * Read a single byte from the stream. * * @return The next byte of the stream. Returns -1 for an EOF condition. * @exception IOException */ public int read() throws IOException { // just get a single byte from the decoder return decoder.decode(in); } /** * Read a buffer of data from the input stream. * * @param buffer The target byte array the data is placed into. * @param offset The starting offset for the read data. * @param length How much data is requested. * * @return The number of bytes of data read. * @exception IOException */ public int read(byte [] buffer, int offset, int length) throws IOException { for (int i = 0; i < length; i++) { int ch = decoder.decode(in); if (ch == -1) { return i == 0 ? -1 : i; } buffer[offset + i] = (byte)ch; } return length; } /** * Indicate whether this stream supports the mark() operation. * * @return Always returns false. */ public boolean markSupported() { return false; } /** * Give an estimate of how much additional data is available * from this stream. * * @return Always returns -1. * @exception IOException */ public int available() throws IOException { // this is almost impossible to determine at this point return -1; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64EncoderStream.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64EncoderStream.jav0000664000175000017500000001456110517560657031732 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; import java.io.FilterOutputStream; /** * An implementation of a FilterOutputStream that encodes the * stream data in BASE64 encoding format. This version does the * encoding "on the fly" rather than encoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class Base64EncoderStream extends FilterOutputStream { // our Filtered stream writes everything out as byte data. This allows the CRLF sequence to // be written with a single call. protected static final byte[] CRLF = { '\r', '\n' }; // our hex encoder utility class. protected Base64Encoder encoder = new Base64Encoder(); // our default for line breaks protected static final int DEFAULT_LINEBREAK = 76; // Data can only be written out in complete units of 3 bytes encoded as 4. Therefore, we need to buffer // as many as 2 bytes to fill out an encoding unit. // the buffered byte count protected int bufferedBytes = 0; // we'll encode this part once it is filled up. protected byte[] buffer = new byte[3]; // the size we process line breaks at. If this is Integer.MAX_VALUE, no line breaks are handled. protected int lineBreak; // the number of encoded characters we've written to the stream, which determines where we // insert line breaks. protected int outputCount; /** * Create a Base64 encoder stream that wraps a specifed stream * using the default line break size. * * @param out The wrapped output stream. */ public Base64EncoderStream(OutputStream out) { this(out, DEFAULT_LINEBREAK); } public Base64EncoderStream(OutputStream out, int lineBreak) { super(out); // lines are processed only in multiple of 4, so round this down. this.lineBreak = (lineBreak / 4) * 4 ; } // in order for this to work, we need to override the 3 different signatures for write public void write(int ch) throws IOException { // store this in the buffer. buffer[bufferedBytes++] = (byte)ch; // if the buffer is filled, encode these bytes if (bufferedBytes == 3) { // check for room in the current line for this character checkEOL(4); // write these directly to the stream. encoder.encode(buffer, 0, 3, out); bufferedBytes = 0; // and update the line length checkers updateLineCount(4); } } public void write(byte [] data) throws IOException { write(data, 0, data.length); } public void write(byte [] data, int offset, int length) throws IOException { // if we have something in the buffer, we need to write enough bytes out to flush // those into the output stream AND continue on to finish off a line. Once we're done there // we can write additional data out in complete blocks. while ((bufferedBytes > 0 || outputCount > 0) && length > 0) { write(data[offset++]); length--; } if (length > 0) { // no linebreaks requested? YES!!!!!, we can just dispose of the lot with one call. if (lineBreak == Integer.MAX_VALUE) { encoder.encode(data, offset, length, out); } else { // calculate the size of a segment we can encode directly as a line. int segmentSize = (lineBreak / 4) * 3; // write this out a block at a time, with separators between. while (length > segmentSize) { // encode a segment encoder.encode(data, offset, segmentSize, out); // write an EOL marker out.write(CRLF); offset += segmentSize; length -= segmentSize; } // any remainder we write out a byte at a time to manage the groupings and // the line count appropriately. if (length > 0) { while (length > 0) { write(data[offset++]); length--; } } } } } public void close() throws IOException { flush(); out.close(); } public void flush() throws IOException { if (bufferedBytes > 0) { encoder.encode(buffer, 0, bufferedBytes, out); bufferedBytes = 0; } } /** * Check for whether we're about the reach the end of our * line limit for an update that's about to occur. If we will * overflow, then a line break is inserted. * * @param required The space required for this pending write. * * @exception IOException */ private void checkEOL(int required) throws IOException { if (lineBreak != Integer.MAX_VALUE) { // if this write would exceed the line maximum, add a linebreak to the stream. if (outputCount + required > lineBreak) { out.write(CRLF); outputCount = 0; } } } /** * Update the counter of characters on the current working line. * This is conditional if we're not working with a line limit. * * @param added The number of characters just added. */ private void updateLineCount(int added) { if (lineBreak != Integer.MAX_VALUE) { outputCount += added; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java0000664000175000017500000002033110517560657030051 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.IOException; /** * Set of utility classes for handling common encoding-related * manipulations. */ public class ASCIIUtil { /** * Test to see if this string contains only US-ASCII (i.e., 7-bit * ASCII) charactes. * * @param s The test string. * * @return true if this is a valid 7-bit ASCII encoding, false if it * contains any non-US ASCII characters. */ static public boolean isAscii(String s) { for (int i = 0; i < s.length(); i++) { if (!isAscii(s.charAt(i))) { return false; } } return true; } /** * Test to see if a given character can be considered "valid" ASCII. * The excluded characters are the control characters less than * 32, 8-bit characters greater than 127, EXCEPT the CR, LF and * tab characters ARE considered value (all less than 32). * * @param ch The test character. * * @return true if this character meets the "ascii-ness" criteria, false * otherwise. */ static public boolean isAscii(int ch) { // these are explicitly considered valid. if (ch == '\r' || ch == '\n' || ch == '\t') { return true; } // anything else outside the range is just plain wrong. if (ch >= 127 || ch < 32) { return false; } return true; } /** * Examine a stream of text and make a judgement on what encoding * type should be used for the text. Ideally, we want to use 7bit * encoding to determine this, but we may need to use either quoted-printable * or base64. The choice is made on the ratio of 7-bit characters to non-7bit. * * @param content An input stream for the content we're examining. * * @exception IOException */ public static String getTextTransferEncoding(InputStream content) throws IOException { // for efficiency, we'll read in blocks. BufferedInputStream in = new BufferedInputStream(content, 4096); int span = 0; // span of characters without a line break. boolean containsLongLines = false; int asciiChars = 0; int nonAsciiChars = 0; while (true) { int ch = in.read(); // if we hit an EOF here, go decide what type we've actually found. if (ch == -1) { break; } // we found a linebreak. Reset the line length counters on either one. We don't // really need to validate here. if (ch == '\n' || ch == '\r') { // hit a line end, reset our line length counter span = 0; } else { span++; // the text has long lines, we can't transfer this as unencoded text. if (span > 998) { containsLongLines = true; } // non-ascii character, we have to transfer this in binary. if (!isAscii(ch)) { nonAsciiChars++; } else { asciiChars++; } } } // looking good so far, only valid chars here. if (nonAsciiChars == 0) { // does this contain long text lines? We need to use a Q-P encoding which will // be only slightly longer, but handles folding the longer lines. if (containsLongLines) { return "quoted-printable"; } else { // ideal! Easiest one to handle. return "7bit"; } } else { // mostly characters requiring encoding? Base64 is our best bet. if (nonAsciiChars > asciiChars) { return "base64"; } else { // Q-P encoding will use fewer bytes than the full Base64. return "quoted-printable"; } } } /** * Examine a stream of text and make a judgement on what encoding * type should be used for the text. Ideally, we want to use 7bit * encoding to determine this, but we may need to use either quoted-printable * or base64. The choice is made on the ratio of 7-bit characters to non-7bit. * * @param content A string for the content we're examining. */ public static String getTextTransferEncoding(String content) { int asciiChars = 0; int nonAsciiChars = 0; for (int i = 0; i < content.length(); i++) { int ch = content.charAt(i); // non-ascii character, we have to transfer this in binary. if (!isAscii(ch)) { nonAsciiChars++; } else { asciiChars++; } } // looking good so far, only valid chars here. if (nonAsciiChars == 0) { // ideal! Easiest one to handle. return "7bit"; } else { // mostly characters requiring encoding? Base64 is our best bet. if (nonAsciiChars > asciiChars) { return "base64"; } else { // Q-P encoding will use fewer bytes than the full Base64. return "quoted-printable"; } } } /** * Determine if the transfer encoding looks like it might be * valid ascii text, and thus transferable as 7bit code. In * order for this to be true, all characters must be valid * 7-bit ASCII code AND all line breaks must be properly formed * (JUST '\r\n' sequences). 7-bit transfers also * typically have a line limit of 1000 bytes (998 + the CRLF), so any * stretch of charactes longer than that will also force Base64 encoding. * * @param content An input stream for the content we're examining. * * @exception IOException */ public static String getBinaryTransferEncoding(InputStream content) throws IOException { // for efficiency, we'll read in blocks. BufferedInputStream in = new BufferedInputStream(content, 4096); int previousChar = 0; int span = 0; // span of characters without a line break. while (true) { int ch = in.read(); // if we hit an EOF here, we've only found valid text so far, so we can transfer this as // 7-bit ascii. if (ch == -1) { return "7bit"; } // we found a newline, this is only valid if the previous char was the '\r' if (ch == '\n') { // malformed linebreak? force this to base64 encoding. if (previousChar != '\r') { return "base64"; } // hit a line end, reset our line length counter span = 0; } else { span++; // the text has long lines, we can't transfer this as unencoded text. if (span > 998) { return "base64"; } // non-ascii character, we have to transfer this in binary. if (!isAscii(ch)) { return "base64"; } } previousChar = ch; } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/UUEncoderStream.java0000664000175000017500000001526210517560657031377 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; /** * An implementation of a FilterOutputStream that encodes the * stream data in UUencoding format. This version does the * encoding "on the fly" rather than encoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class UUEncoderStream extends FilterOutputStream { // default values included on the "begin" prefix of the data stream. protected static final int DEFAULT_MODE = 644; protected static final String DEFAULT_NAME = "encoder.buf"; protected static final int MAX_CHARS_PER_LINE = 45; // the configured name on the "begin" command. protected String name; // the configured mode for the "begin" command. protected int mode; // since this is a filtering stream, we need to wait until we have the first byte written for encoding // to write out the "begin" marker. A real pain, but necessary. protected boolean beginWritten = false; // our encoder utility class. protected UUEncoder encoder = new UUEncoder(); // Data is generally written out in 45 character lines, so we're going to buffer that amount before // asking the encoder to process this. // the buffered byte count protected int bufferedBytes = 0; // we'll encode this part once it is filled up. protected byte[] buffer = new byte[45]; /** * Create a Base64 encoder stream that wraps a specifed stream * using the default line break size. * * @param out The wrapped output stream. */ public UUEncoderStream(OutputStream out) { this(out, DEFAULT_NAME, DEFAULT_MODE); } /** * Create a Base64 encoder stream that wraps a specifed stream * using the default line break size. * * @param out The wrapped output stream. * @param name The filename placed on the "begin" command. */ public UUEncoderStream(OutputStream out, String name) { this(out, name, DEFAULT_MODE); } public UUEncoderStream(OutputStream out, String name, int mode) { super(out); // fill in the name and mode information. this.name = name; this.mode = mode; } private void checkBegin() throws IOException { if (!beginWritten) { // grumble...OutputStream doesn't directly support writing String data. We'll wrap this in // a PrintStream() to accomplish the task of writing the begin command. PrintStream writer = new PrintStream(out); // write out the stream with a CRLF marker writer.print("begin " + mode + " " + name + "\r\n"); writer.flush(); beginWritten = true; } } private void writeEnd() throws IOException { PrintStream writer = new PrintStream(out); // write out the stream with a CRLF marker writer.print("\nend\r\n"); writer.flush(); } private void flushBuffer() throws IOException { // make sure we've written the begin marker first checkBegin(); // ask the encoder to encode and write this out. if (bufferedBytes != 0) { encoder.encode(buffer, 0, bufferedBytes, out); // reset the buffer count bufferedBytes = 0; } } private int bufferSpace() { return MAX_CHARS_PER_LINE - bufferedBytes; } private boolean isBufferFull() { return bufferedBytes >= MAX_CHARS_PER_LINE; } // in order for this to work, we need to override the 3 different signatures for write public void write(int ch) throws IOException { // store this in the buffer. buffer[bufferedBytes++] = (byte)ch; // if we filled this up, time to encode and write to the output stream. if (isBufferFull()) { flushBuffer(); } } public void write(byte [] data) throws IOException { write(data, 0, data.length); } public void write(byte [] data, int offset, int length) throws IOException { // first check to see how much space we have left in the buffer, and copy that over int copyBytes = Math.min(bufferSpace(), length); System.arraycopy(buffer, bufferedBytes, data, offset, copyBytes); bufferedBytes += copyBytes; offset += copyBytes; length -= copyBytes; // if we filled this up, time to encode and write to the output stream. if (isBufferFull()) { flushBuffer(); } // we've flushed the leading part up to the line break. Now if we have complete lines // of data left, we can have the encoder process all of these lines directly. if (length >= MAX_CHARS_PER_LINE) { int fullLinesLength = (length / MAX_CHARS_PER_LINE) * MAX_CHARS_PER_LINE; // ask the encoder to encode and write this out. encoder.encode(data, offset, fullLinesLength, out); offset += fullLinesLength; length -= fullLinesLength; } // ok, now we're down to a potential trailing bit we need to move into the // buffer for later processing. if (length > 0) { System.arraycopy(buffer, 0, data, offset, length); bufferedBytes += length; offset += length; length -= length; } } public void flush() throws IOException { // flush any unencoded characters we're holding. flushBuffer(); // write out the data end marker writeEnd(); // and flush the output stream too so that this data is available. out.flush(); } public void close() throws IOException { // flush all of the streams and close the target output stream. flush(); out.close(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/HexEncoder.java0000664000175000017500000001124510517560657030413 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; public class HexEncoder implements Encoder { protected final byte[] encodingTable = { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f' }; /* * set up the decoding table. */ protected final byte[] decodingTable = new byte[128]; protected void initialiseDecodingTable() { for (int i = 0; i < encodingTable.length; i++) { decodingTable[encodingTable[i]] = (byte)i; } decodingTable['A'] = decodingTable['a']; decodingTable['B'] = decodingTable['b']; decodingTable['C'] = decodingTable['c']; decodingTable['D'] = decodingTable['d']; decodingTable['E'] = decodingTable['e']; decodingTable['F'] = decodingTable['f']; } public HexEncoder() { initialiseDecodingTable(); } /** * encode the input data producing a Hex output stream. * * @return the number of bytes produced. */ public int encode( byte[] data, int off, int length, OutputStream out) throws IOException { for (int i = off; i < (off + length); i++) { int v = data[i] & 0xff; out.write(encodingTable[(v >>> 4)]); out.write(encodingTable[v & 0xf]); } return length * 2; } private boolean ignore( char c) { return (c == '\n' || c =='\r' || c == '\t' || c == ' '); } /** * decode the Hex encoded byte data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public int decode( byte[] data, int off, int length, OutputStream out) throws IOException { byte[] bytes; byte b1, b2; int outLen = 0; int end = off + length; while (end > 0) { if (!ignore((char)data[end - 1])) { break; } end--; } int i = off; while (i < end) { while (i < end && ignore((char)data[i])) { i++; } b1 = decodingTable[data[i++]]; while (i < end && ignore((char)data[i])) { i++; } b2 = decodingTable[data[i++]]; out.write((b1 << 4) | b2); outLen++; } return outLen; } /** * decode the Hex encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public int decode( String data, OutputStream out) throws IOException { byte[] bytes; byte b1, b2, b3, b4; int length = 0; int end = data.length(); while (end > 0) { if (!ignore(data.charAt(end - 1))) { break; } end--; } int i = 0; while (i < end) { while (i < end && ignore(data.charAt(i))) { i++; } b1 = decodingTable[data.charAt(i++)]; while (i < end && ignore(data.charAt(i))) { i++; } b2 = decodingTable[data.charAt(i++)]; out.write((b1 << 4) | b2); length++; } return length; } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableEncoderStream.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableEncoderS0000664000175000017500000000534310517560657032176 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; import java.io.FilterOutputStream; /** * An implementation of a FilterOutputStream that encodes the * stream data in Q-P encoding format. This version does the * encoding "on the fly" rather than encoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class QuotedPrintableEncoderStream extends FilterOutputStream { // our hex encoder utility class. protected QuotedPrintableEncoder encoder; // our default for line breaks protected static final int DEFAULT_LINEBREAK = 76; // the instance line break value protected int lineBreak; /** * Create a Base64 encoder stream that wraps a specifed stream * using the default line break size. * * @param out The wrapped output stream. */ public QuotedPrintableEncoderStream(OutputStream out) { this(out, DEFAULT_LINEBREAK); } public QuotedPrintableEncoderStream(OutputStream out, int lineBreak) { super(out); // lines are processed only in multiple of 4, so round this down. this.lineBreak = (lineBreak / 4) * 4 ; // create an encoder configured to this amount encoder = new QuotedPrintableEncoder(out, this.lineBreak); } public void write(int ch) throws IOException { // have the encoder do the heavy lifting here. encoder.encode(ch); } public void write(byte [] data) throws IOException { write(data, 0, data.length); } public void write(byte [] data, int offset, int length) throws IOException { // the encoder does the heavy lifting here. encoder.encode(data, offset, length); } public void close() throws IOException { out.close(); } public void flush() throws IOException { out.flush(); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Hex.java0000664000175000017500000000770610517560657027122 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public class Hex { private static final Encoder encoder = new HexEncoder(); /** * encode the input data producing a Hex encoded byte array. * * @return a byte array containing the Hex encoded data. */ public static byte[] encode( byte[] data) { return encode(data, 0, data.length); } /** * encode the input data producing a Hex encoded byte array. * * @return a byte array containing the Hex encoded data. */ public static byte[] encode( byte[] data, int off, int length) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.encode(data, off, length, bOut); } catch (IOException e) { throw new RuntimeException("exception encoding Hex string: " + e); } return bOut.toByteArray(); } /** * Hex encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * Hex encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, int off, int length, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * decode the Hex encoded input data. It is assumed the input data is valid. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, 0, data.length, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding Hex string: " + e); } return bOut.toByteArray(); } /** * decode the Hex encoded String data - whitespace will be ignored. * * @return a byte array representing the decoded data. */ public static byte[] decode( String data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding Hex string: " + e); } return bOut.toByteArray(); } /** * decode the Hex encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public static int decode( String data, OutputStream out) throws IOException { return encoder.decode(data, out); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.jav0000664000175000017500000001626011064163306031702 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.InputStream; import java.io.FilterInputStream; /** * An implementation of a FilterInputStream that decodes the * stream data in BASE64 encoding format. This version does the * decoding "on the fly" rather than decoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class Base64DecoderStream extends FilterInputStream { static protected final String MAIL_BASE64_IGNOREERRORS = "mail.mime.base64.ignoreerrors"; // number of decodeable units we'll try to process at one time. We'll attempt to read that much // data from the input stream and decode in blocks. static protected final int BUFFERED_UNITS = 2000; // our decoder for processing the data protected Base64Encoder decoder = new Base64Encoder(); // can be overridden by a system property. protected boolean ignoreErrors = false; // buffer for reading in chars for decoding (which can support larger bulk reads) protected byte[] encodedChars = new byte[BUFFERED_UNITS * 4]; // a buffer for one decoding unit's worth of data (3 bytes). This is the minimum amount we // can read at one time. protected byte[] decodedChars = new byte[BUFFERED_UNITS * 3]; // count of characters in the buffer protected int decodedCount = 0; // index of the next decoded character protected int decodedIndex = 0; public Base64DecoderStream(InputStream in) { super(in); // make sure we get the ignore errors flag ignoreErrors = SessionUtil.getBooleanProperty(MAIL_BASE64_IGNOREERRORS, false); } /** * Test for the existance of decoded characters in our buffer * of decoded data. * * @return True if we currently have buffered characters. */ private boolean dataAvailable() { return decodedCount != 0; } /** * Get the next buffered decoded character. * * @return The next decoded character in the buffer. */ private byte getBufferedChar() { decodedCount--; return decodedChars[decodedIndex++]; } /** * Decode a requested number of bytes of data into a buffer. * * @return true if we were able to obtain more data, false otherwise. */ private boolean decodeStreamData() throws IOException { decodedIndex = 0; // fill up a data buffer with input data int readCharacters = fillEncodedBuffer(); if (readCharacters > 0) { decodedCount = decoder.decode(encodedChars, 0, readCharacters, decodedChars); return true; } return false; } /** * Retrieve a single byte from the decoded characters buffer. * * @return The decoded character or -1 if there was an EOF condition. */ private int getByte() throws IOException { if (!dataAvailable()) { if (!decodeStreamData()) { return -1; } } decodedCount--; // we need to ensure this doesn't get sign extended return decodedChars[decodedIndex++] & 0xff; } private int getBytes(byte[] data, int offset, int length) throws IOException { int readCharacters = 0; while (length > 0) { // need data? Try to get some if (!dataAvailable()) { // if we can't get this, return a count of how much we did get (which may be -1). if (!decodeStreamData()) { return readCharacters > 0 ? readCharacters : -1; } } // now copy some of the data from the decoded buffer to the target buffer int copyCount = Math.min(decodedCount, length); System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount); decodedIndex += copyCount; decodedCount -= copyCount; offset += copyCount; length -= copyCount; readCharacters += copyCount; } return readCharacters; } /** * Fill our buffer of input characters for decoding from the * stream. This will attempt read a full buffer, but will * terminate on an EOF or read error. This will filter out * non-Base64 encoding chars and will only return a valid * multiple of 4 number of bytes. * * @return The count of characters read. */ private int fillEncodedBuffer() throws IOException { int readCharacters = 0; while (true) { // get the next character from the stream int ch = in.read(); // did we hit an EOF condition? if (ch == -1) { // now check to see if this is normal, or potentially an error // if we didn't get characters as a multiple of 4, we may need to complain about this. if ((readCharacters % 4) != 0) { // the error checking can be turned off...normally it isn't if (!ignoreErrors) { throw new IOException("Base64 encoding error, data truncated"); } // we're ignoring errors, so round down to a multiple and return that. return (readCharacters / 4) * 4; } // return the count. return readCharacters; } // if this character is valid in a Base64 stream, copy it to the buffer. else if (decoder.isValidBase64(ch)) { encodedChars[readCharacters++] = (byte)ch; // if we've filled up the buffer, time to quit. if (readCharacters >= encodedChars.length) { return readCharacters; } } // we're filtering out whitespace and CRLF characters, so just ignore these } } // in order to function as a filter, these streams need to override the different // read() signature. public int read() throws IOException { return getByte(); } public int read(byte [] buffer, int offset, int length) throws IOException { return getBytes(buffer, offset, length); } public boolean markSupported() { return false; } public int available() throws IOException { return ((in.available() / 4) * 3) + decodedCount; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintable.java0000664000175000017500000001214310517560657031467 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class QuotedPrintable { // NOTE: the QuotedPrintableEncoder class needs to keep some active state about what's going on with // respect to line breaks and significant white spaces. This makes it difficult to keep one static // instance of the decode around for reuse. /** * encode the input data producing a Q-P encoded byte array. * * @return a byte array containing the Q-P encoded data. */ public static byte[] encode( byte[] data) { return encode(data, 0, data.length); } /** * encode the input data producing a Q-P encoded byte array. * * @return a byte array containing the Q-P encoded data. */ public static byte[] encode( byte[] data, int off, int length) { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.encode(data, off, length, bOut); } catch (IOException e) { throw new RuntimeException("exception encoding Q-P encoded string: " + e); } return bOut.toByteArray(); } /** * Q-P encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, OutputStream out) throws IOException { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); return encoder.encode(data, 0, data.length, out); } /** * Q-P encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, int off, int length, OutputStream out) throws IOException { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); return encoder.encode(data, 0, data.length, out); } /** * decode the Q-P encoded input data. It is assumed the input data is valid. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); try { encoder.decode(data, 0, data.length, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding Q-P encoded string: " + e); } return bOut.toByteArray(); } /** * decode the UUEncided String data. * * @return a byte array representing the decoded data. */ public static byte[] decode( String data) { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding Q-P encoded string: " + e); } return bOut.toByteArray(); } /** * decode the Q-P encoded encoded String data writing it to the given output stream. * * @return the number of bytes produced. */ public static int decode( String data, OutputStream out) throws IOException { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); return encoder.decode(data, out); } /** * decode the base Q-P encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @param data The array data to decode. * @param out The output stream for the data. * * @return the number of bytes produced. * @exception IOException */ public static int decode(byte [] data, OutputStream out) throws IOException { QuotedPrintableEncoder encoder = new QuotedPrintableEncoder(); return encoder.decode(data, 0, data.length, out); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/XTextEncoder.java0000664000175000017500000001047010517560657030742 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; public class XTextEncoder implements Encoder { protected final byte[] encodingTable = { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' }; /* * set up the decoding table. */ protected final byte[] decodingTable = new byte[128]; protected void initialiseDecodingTable() { for (int i = 0; i < encodingTable.length; i++) { decodingTable[encodingTable[i]] = (byte)i; } } public XTextEncoder() { initialiseDecodingTable(); } /** * encode the input data producing an XText output stream. * * @return the number of bytes produced. */ public int encode( byte[] data, int off, int length, OutputStream out) throws IOException { int bytesWritten = 0; for (int i = off; i < (off + length); i++) { int v = data[i] & 0xff; // character tha must be encoded? Prefix with a '+' and encode in hex. if (v < 33 || v > 126 || v == '+' || v == '+') { out.write((byte)'+'); out.write(encodingTable[(v >>> 4)]); out.write(encodingTable[v & 0xf]); bytesWritten += 3; } else { // add unchanged. out.write((byte)v); bytesWritten++; } } return bytesWritten; } /** * decode the xtext encoded byte data writing it to the given output stream * * @return the number of bytes produced. */ public int decode( byte[] data, int off, int length, OutputStream out) throws IOException { byte[] bytes; byte b1, b2; int outLen = 0; int end = off + length; int i = off; while (i < end) { byte v = data[i++]; // a plus is a hex character marker, need to decode a hex value. if (v == '+') { b1 = decodingTable[data[i++]]; b2 = decodingTable[data[i++]]; out.write((b1 << 4) | b2); } else { // copied over unchanged. out.write(v); } // always just one byte added outLen++; } return outLen; } /** * decode the xtext encoded String data writing it to the given output stream. * * @return the number of bytes produced. */ public int decode( String data, OutputStream out) throws IOException { byte[] bytes; byte b1, b2, b3, b4; int length = 0; int end = data.length(); int i = 0; while (i < end) { char v = data.charAt(i++); if (v == '+') { b1 = decodingTable[data.charAt(i++)]; b2 = decodingTable[data.charAt(i++)]; out.write((b1 << 4) | b2); } else { out.write((byte)v); } length++; } return length; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java0000664000175000017500000004560210754633212030706 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; public class Base64Encoder implements Encoder { protected final byte[] encodingTable = { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' }; protected byte padding = (byte)'='; /* * set up the decoding table. */ protected final byte[] decodingTable = new byte[256]; protected void initialiseDecodingTable() { for (int i = 0; i < encodingTable.length; i++) { decodingTable[encodingTable[i]] = (byte)i; } } public Base64Encoder() { initialiseDecodingTable(); } /** * encode the input data producing a base 64 output stream. * * @return the number of bytes produced. */ public int encode( byte[] data, int off, int length, OutputStream out) throws IOException { int modulus = length % 3; int dataLength = (length - modulus); int a1, a2, a3; for (int i = off; i < off + dataLength; i += 3) { a1 = data[i] & 0xff; a2 = data[i + 1] & 0xff; a3 = data[i + 2] & 0xff; out.write(encodingTable[(a1 >>> 2) & 0x3f]); out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); out.write(encodingTable[a3 & 0x3f]); } /* * process the tail end. */ int b1, b2, b3; int d1, d2; switch (modulus) { case 0: /* nothing left to do */ break; case 1: d1 = data[off + dataLength] & 0xff; b1 = (d1 >>> 2) & 0x3f; b2 = (d1 << 4) & 0x3f; out.write(encodingTable[b1]); out.write(encodingTable[b2]); out.write(padding); out.write(padding); break; case 2: d1 = data[off + dataLength] & 0xff; d2 = data[off + dataLength + 1] & 0xff; b1 = (d1 >>> 2) & 0x3f; b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; b3 = (d2 << 2) & 0x3f; out.write(encodingTable[b1]); out.write(encodingTable[b2]); out.write(encodingTable[b3]); out.write(padding); break; } return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); } private boolean ignore( char c) { return (c == '\n' || c =='\r' || c == '\t' || c == ' '); } /** * decode the base 64 encoded byte data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public int decode( byte[] data, int off, int length, OutputStream out) throws IOException { byte[] bytes; byte b1, b2, b3, b4; int outLen = 0; int end = off + length; while (end > 0) { if (!ignore((char)data[end - 1])) { break; } end--; } int i = off; int finish = end - 4; while (i < finish) { while ((i < finish) && ignore((char)data[i])) { i++; } b1 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b2 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b3 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b4 = decodingTable[data[i++]]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); out.write((b3 << 6) | b4); outLen += 3; } if (data[end - 2] == padding) { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; out.write((b1 << 2) | (b2 >> 4)); outLen += 1; } else if (data[end - 1] == padding) { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; b3 = decodingTable[data[end - 2]]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); outLen += 2; } else { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; b3 = decodingTable[data[end - 2]]; b4 = decodingTable[data[end - 1]]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); out.write((b3 << 6) | b4); outLen += 3; } return outLen; } /** * decode the base 64 encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public int decode( String data, OutputStream out) throws IOException { byte[] bytes; byte b1, b2, b3, b4; int length = 0; int end = data.length(); while (end > 0) { if (!ignore(data.charAt(end - 1))) { break; } end--; } int i = 0; int finish = end - 4; while (i < finish) { while ((i < finish) && ignore(data.charAt(i))) { i++; } b1 = decodingTable[data.charAt(i++)]; while ((i < finish) && ignore(data.charAt(i))) { i++; } b2 = decodingTable[data.charAt(i++)]; while ((i < finish) && ignore(data.charAt(i))) { i++; } b3 = decodingTable[data.charAt(i++)]; while ((i < finish) && ignore(data.charAt(i))) { i++; } b4 = decodingTable[data.charAt(i++)]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); out.write((b3 << 6) | b4); length += 3; } if (data.charAt(end - 2) == padding) { b1 = decodingTable[data.charAt(end - 4)]; b2 = decodingTable[data.charAt(end - 3)]; out.write((b1 << 2) | (b2 >> 4)); length += 1; } else if (data.charAt(end - 1) == padding) { b1 = decodingTable[data.charAt(end - 4)]; b2 = decodingTable[data.charAt(end - 3)]; b3 = decodingTable[data.charAt(end - 2)]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); length += 2; } else { b1 = decodingTable[data.charAt(end - 4)]; b2 = decodingTable[data.charAt(end - 3)]; b3 = decodingTable[data.charAt(end - 2)]; b4 = decodingTable[data.charAt(end - 1)]; out.write((b1 << 2) | (b2 >> 4)); out.write((b2 << 4) | (b3 >> 2)); out.write((b3 << 6) | b4); length += 3; } return length; } /** * decode the base 64 encoded byte data writing it to the provided byte array buffer. * * @return the number of bytes produced. */ public int decode(byte[] data, int off, int length, byte[] out) throws IOException { byte[] bytes; byte b1, b2, b3, b4; int outLen = 0; int end = off + length; while (end > 0) { if (!ignore((char)data[end - 1])) { break; } end--; } int i = off; int finish = end - 4; while (i < finish) { while ((i < finish) && ignore((char)data[i])) { i++; } b1 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b2 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b3 = decodingTable[data[i++]]; while ((i < finish) && ignore((char)data[i])) { i++; } b4 = decodingTable[data[i++]]; out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); out[outLen++] = (byte)((b3 << 6) | b4); } if (data[end - 2] == padding) { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); } else if (data[end - 1] == padding) { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; b3 = decodingTable[data[end - 2]]; out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); } else { b1 = decodingTable[data[end - 4]]; b2 = decodingTable[data[end - 3]]; b3 = decodingTable[data[end - 2]]; b4 = decodingTable[data[end - 1]]; out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); out[outLen++] = (byte)((b3 << 6) | b4); } return outLen; } /** * Test if a character is a valid Base64 encoding character. This * must be either a valid digit or the padding character ("="). * * @param ch The test character. * * @return true if this is valid in Base64 encoded data, false otherwise. */ public boolean isValidBase64(int ch) { // 'A' has the value 0 in the decoding table, so we need a special one for that return ch == padding || ch == 'A' || decodingTable[ch] != 0; } /** * Perform RFC-2047 word encoding using Base64 data encoding. * * @param in The source for the encoded data. * @param charset The charset tag to be added to each encoded data section. * @param out The output stream where the encoded data is to be written. * @param fold Controls whether separate sections of encoded data are separated by * linebreaks or whitespace. * * @exception IOException */ public void encodeWord(InputStream in, String charset, OutputStream out, boolean fold) throws IOException { PrintStream writer = new PrintStream(out); // encoded words are restricted to 76 bytes, including the control adornments. int limit = 75 - 7 - charset.length(); boolean firstLine = true; StringBuffer encodedString = new StringBuffer(76); while (true) { // encode the next segment. encode(in, encodedString, limit); // if we're out of data, nothing will be encoded. if (encodedString.length() == 0) { break; } // if we have more than one segment, we need to insert separators. Depending on whether folding // was requested, this is either a blank or a linebreak. if (!firstLine) { if (fold) { writer.print("\r\n"); } else { writer.print(" "); } } // add the encoded word header writer.print("=?"); writer.print(charset); writer.print("?B?"); // the data writer.print(encodedString.toString()); // and the word terminator. writer.print("?="); writer.flush(); // reset our string buffer for the next segment. encodedString.setLength(0); // we need a delimiter after this firstLine = false; } } /** * Perform RFC-2047 word encoding using Base64 data encoding. * * @param in The source for the encoded data. * @param charset The charset tag to be added to each encoded data section. * @param out The output stream where the encoded data is to be written. * @param fold Controls whether separate sections of encoded data are separated by * linebreaks or whitespace. * * @exception IOException */ public void encodeWord(byte[] data, StringBuffer out, String charset) throws IOException { // append the word header out.append("=?"); out.append(charset); out.append("?B?"); // add on the encodeded data encodeWordData(data, out); // the end of the encoding marker out.append("?="); } /** * encode the input data producing a base 64 output stream. * * @return the number of bytes produced. */ public void encodeWordData(byte[] data, StringBuffer out) { int modulus = data.length % 3; int dataLength = (data.length - modulus); int a1, a2, a3; for (int i = 0; i < dataLength; i += 3) { a1 = data[i] & 0xff; a2 = data[i + 1] & 0xff; a3 = data[i + 2] & 0xff; out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); out.append((char)encodingTable[a3 & 0x3f]); } /* * process the tail end. */ int b1, b2, b3; int d1, d2; switch (modulus) { case 0: /* nothing left to do */ break; case 1: d1 = data[dataLength] & 0xff; b1 = (d1 >>> 2) & 0x3f; b2 = (d1 << 4) & 0x3f; out.append((char)encodingTable[b1]); out.append((char)encodingTable[b2]); out.append((char)padding); out.append((char)padding); break; case 2: d1 = data[dataLength] & 0xff; d2 = data[dataLength + 1] & 0xff; b1 = (d1 >>> 2) & 0x3f; b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; b3 = (d2 << 2) & 0x3f; out.append((char)encodingTable[b1]); out.append((char)encodingTable[b2]); out.append((char)encodingTable[b3]); out.append((char)padding); break; } } /** * encode the input data producing a base 64 output stream. * * @return the number of bytes produced. */ public void encode(InputStream in, StringBuffer out, int limit) throws IOException { int count = limit / 4; byte [] inBuffer = new byte[3]; while (count-- > 0) { int readCount = in.read(inBuffer); // did we get a full triplet? that's an easy encoding. if (readCount == 3) { int a1 = inBuffer[0] & 0xff; int a2 = inBuffer[1] & 0xff; int a3 = inBuffer[2] & 0xff; out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); out.append((char)encodingTable[a3 & 0x3f]); } else if (readCount <= 0) { // eof condition, don'e entirely. return; } else if (readCount == 1) { int a1 = inBuffer[0] & 0xff; out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); out.append((char)encodingTable[(a1 << 4) & 0x3f]); out.append((char)padding); out.append((char)padding); return; } else if (readCount == 2) { int a1 = inBuffer[0] & 0xff; int a2 = inBuffer[1] & 0xff; out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); out.append((char)encodingTable[(a2 << 2) & 0x3f]); out.append((char)padding); return; } } } /** * Estimate the final encoded size of a segment of data. * This is used to ensure that the encoded blocks do * not get split across a unicode character boundary and * that the encoding will fit within the bounds of * a mail header line. * * @param data The data we're anticipating encoding. * * @return The size of the byte data in encoded form. */ public int estimateEncodedLength(byte[] data) { return ((data.length + 2) / 3) * 4; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/UUEncode.java0000664000175000017500000000761010517560657030037 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public class UUEncode { private static final Encoder encoder = new UUEncoder(); /** * encode the input data producing a UUEncoded byte array. * * @return a byte array containing the UUEncoded data. */ public static byte[] encode( byte[] data) { return encode(data, 0, data.length); } /** * encode the input data producing a UUEncoded byte array. * * @return a byte array containing the UUEncoded data. */ public static byte[] encode( byte[] data, int off, int length) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.encode(data, off, length, bOut); } catch (IOException e) { throw new RuntimeException("exception encoding UUEncoded string: " + e); } return bOut.toByteArray(); } /** * UUEncode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * UUEncode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, int off, int length, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * decode the UUEncoded input data. It is assumed the input data is valid. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, 0, data.length, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding UUEncoded string: " + e); } return bOut.toByteArray(); } /** * decode the UUEncided String data. * * @return a byte array representing the decoded data. */ public static byte[] decode( String data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding UUEncoded string: " + e); } return bOut.toByteArray(); } /** * decode the UUEncoded encoded String data writing it to the given output stream. * * @return the number of bytes produced. */ public static int decode( String data, OutputStream out) throws IOException { return encoder.decode(data, out); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/RFC2231Encoder.java0000664000175000017500000001702410517560657030612 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import javax.mail.internet.MimeUtility; /** * Encoder for RFC2231 encoded parameters * * RFC2231 string are encoded as * * charset'language'encoded-text * * and * * encoded-text = *(char / hexchar) * * where * * char is any ASCII character in the range 33-126, EXCEPT * the characters "%" and " ". * * hexchar is an ASCII "%" followed by two upper case * hexadecimal digits. */ public class RFC2231Encoder implements Encoder { protected final byte[] encodingTable = { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' }; protected String DEFAULT_SPECIALS = " *'%"; protected String specials = DEFAULT_SPECIALS; /* * set up the decoding table. */ protected final byte[] decodingTable = new byte[128]; protected void initialiseDecodingTable() { for (int i = 0; i < encodingTable.length; i++) { decodingTable[encodingTable[i]] = (byte)i; } } public RFC2231Encoder() { this(null); } public RFC2231Encoder(String specials) { if (specials != null) { this.specials = DEFAULT_SPECIALS + specials; } initialiseDecodingTable(); } /** * encode the input data producing an RFC2231 output stream. * * @return the number of bytes produced. */ public int encode(byte[] data, int off, int length, OutputStream out) throws IOException { int bytesWritten = 0; for (int i = off; i < (off + length); i++) { int ch = data[i] & 0xff; // character tha must be encoded? Prefix with a '%' and encode in hex. if (ch <= 32 || ch >= 127 || specials.indexOf(ch) != -1) { out.write((byte)'%'); out.write(encodingTable[ch >> 4]); out.write(encodingTable[ch & 0xf]); bytesWritten += 3; } else { // add unchanged. out.write((byte)ch); bytesWritten++; } } return bytesWritten; } /** * decode the RFC2231 encoded byte data writing it to the given output stream * * @return the number of bytes produced. */ public int decode(byte[] data, int off, int length, OutputStream out) throws IOException { int outLen = 0; int end = off + length; int i = off; while (i < end) { byte v = data[i++]; // a percent is a hex character marker, need to decode a hex value. if (v == '%') { byte b1 = decodingTable[data[i++]]; byte b2 = decodingTable[data[i++]]; out.write((b1 << 4) | b2); } else { // copied over unchanged. out.write(v); } // always just one byte added outLen++; } return outLen; } /** * decode the RFC2231 encoded String data writing it to the given output stream. * * @return the number of bytes produced. */ public int decode(String data, OutputStream out) throws IOException { int length = 0; int end = data.length(); int i = 0; while (i < end) { char v = data.charAt(i++); if (v == '%') { byte b1 = decodingTable[data.charAt(i++)]; byte b2 = decodingTable[data.charAt(i++)]; out.write((b1 << 4) | b2); } else { out.write((byte)v); } length++; } return length; } /** * Encode a string as an RFC2231 encoded parameter, using the * given character set and language. * * @param charset The source character set (the MIME version). * @param language The encoding language. * @param data The data to encode. * * @return The encoded string. */ public String encode(String charset, String language, String data) throws IOException { byte[] bytes = null; try { // the charset we're adding is the MIME-defined name. We need the java version // in order to extract the bytes. bytes = data.getBytes(MimeUtility.javaCharset(charset)); } catch (UnsupportedEncodingException e) { // we have a translation problem here. return null; } StringBuffer result = new StringBuffer(); // append the character set, if we have it. if (charset != null) { result.append(charset); } // the field marker is required. result.append("'"); // and the same for the language. if (language != null) { result.append(language); } // the field marker is required. result.append("'"); // wrap an output stream around our buffer for the decoding OutputStream out = new StringBufferOutputStream(result); // encode the data stream encode(bytes, 0, bytes.length, out); // finis! return result.toString(); } /** * Decode an RFC2231 encoded string. * * @param data The data to decode. * * @return The decoded string. * @exception IOException * @exception UnsupportedEncodingException */ public String decode(String data) throws IOException, UnsupportedEncodingException { // get the end of the language field int charsetEnd = data.indexOf('\''); // uh oh, might not be there if (charsetEnd == -1) { throw new IOException("Missing charset in RFC2231 encoded value"); } String charset = data.substring(0, charsetEnd); // now pull out the language the same way int languageEnd = data.indexOf('\'', charsetEnd + 1); if (languageEnd == -1) { throw new IOException("Missing language in RFC2231 encoded value"); } String language = data.substring(charsetEnd + 1, languageEnd); ByteArrayOutputStream out = new ByteArrayOutputStream(data.length()); // decode the data decode(data.substring(languageEnd + 1), out); byte[] bytes = out.toByteArray(); // build a new string from this using the java version of the encoded charset. return new String(bytes, 0, bytes.length, MimeUtility.javaCharset(charset)); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Encoder.java0000664000175000017500000000236710517560657027753 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; /** * Encode and decode byte arrays (typically from binary to 7-bit ASCII * encodings). */ public interface Encoder { int encode(byte[] data, int off, int length, OutputStream out) throws IOException; int decode(byte[] data, int off, int length, OutputStream out) throws IOException; int decode(String data, OutputStream out) throws IOException; } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableEncoder.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/QuotedPrintableEncoder.0000664000175000017500000006576611027703453032136 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PushbackInputStream; import java.io.UnsupportedEncodingException; public class QuotedPrintableEncoder implements Encoder { static protected final byte[] encodingTable = { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' }; /* * set up the decoding table. */ static protected final byte[] decodingTable = new byte[128]; static { // initialize the decoding table for (int i = 0; i < encodingTable.length; i++) { decodingTable[encodingTable[i]] = (byte)i; } } // default number of characters we will write per line. static private final int DEFAULT_CHARS_PER_LINE = 76; // the output stream we're wrapped around protected OutputStream out; // the number of bytes written; protected int bytesWritten = 0; // number of bytes written on the current line protected int lineCount = 0; // line length we're dealing with protected int lineLength; // number of deferred whitespace characters in decode mode. protected int deferredWhitespace = 0; protected int cachedCharacter = -1; // indicates whether the last character was a '\r', potentially part of a CRLF sequence. protected boolean lastCR = false; // remember whether last character was a white space. protected boolean lastWhitespace = false; public QuotedPrintableEncoder() { this(null, DEFAULT_CHARS_PER_LINE); } public QuotedPrintableEncoder(OutputStream out) { this(out, DEFAULT_CHARS_PER_LINE); } public QuotedPrintableEncoder(OutputStream out, int lineLength) { this.out = out; this.lineLength = lineLength; } private void checkDeferred(int ch) throws IOException { // was the last character we looked at a whitespace? Try to decide what to do with it now. if (lastWhitespace) { // if this whitespace is at the end of the line, write it out encoded if (ch == '\r' || ch == '\n') { writeEncodedCharacter(' '); } else { // we can write this out without encoding. writeCharacter(' '); } // we always turn this off. lastWhitespace = false; } // deferred carriage return? else if (lastCR) { // if the char following the CR was not a new line, write an EOL now. if (ch != '\n') { writeEOL(); } // we always turn this off too lastCR = false; } } /** * encode the input data producing a UUEncoded output stream. * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * * @return the number of bytes produced. */ public int encode(byte[] data, int off, int length) throws IOException { int endOffset = off + length; while (off < endOffset) { // get the character byte ch = data[off++]; // handle the encoding of this character. encode(ch); } return bytesWritten; } public void encode(int ch) throws IOException { // make sure this is just a single byte value. ch = ch &0xFF; // see if we had to defer handling of a whitespace or '\r' character, and handle it if necessary. checkDeferred(ch); // different characters require special handling. switch (ch) { // spaces require special handling. If the next character is a line terminator, then // the space needs to be encoded. case ' ': { // at this point, we don't know whether this needs encoding or not. If the next // character is a linend, it gets encoded. If anything else, we just write it as is. lastWhitespace = true; // turn off any CR flags. lastCR = false; break; } // carriage return, which may be part of a CRLF sequence. case '\r': { // just flag this until we see the next character. lastCR = true; break; } // a new line character...we need to check to see if it was paired up with a '\r' char. case '\n': { // we always write this out for a newline. We defer CRs until we see if the LF follows. writeEOL(); break; } // an '=' is the escape character for an encoded character, so it must also // be written encoded. case '=': { writeEncodedCharacter(ch); break; } // all other characters. If outside the printable character range, write it encoded. default: { if (ch < 32 || ch >= 127) { writeEncodedCharacter(ch); } else { writeCharacter(ch); } break; } } } /** * encode the input data producing a UUEncoded output stream. * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * * @return the number of bytes produced. */ public int encode(byte[] data, int off, int length, String specials) throws IOException { int endOffset = off + length; while (off < endOffset) { // get the character byte ch = data[off++]; // handle the encoding of this character. encode(ch, specials); } return bytesWritten; } /** * encode the input data producing a UUEncoded output stream. * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * * @return the number of bytes produced. */ public int encode(PushbackInputStream in, StringBuffer out, String specials, int limit) throws IOException { int count = 0; while (count < limit) { int ch = in.read(); if (ch == -1) { return count; } // make sure this is just a single byte value. ch = ch &0xFF; // spaces require special handling. If the next character is a line terminator, then // the space needs to be encoded. if (ch == ' ') { // blanks get translated into underscores, because the encoded tokens can't have embedded blanks. out.append('_'); count++; } // non-ascii chars and the designated specials all get encoded. else if (ch < 32 || ch >= 127 || specials.indexOf(ch) != -1) { // we need at least 3 characters to write this out, so we need to // forget we saw this one and try in the next segment. if (count + 3 > limit) { in.unread(ch); return count; } out.append('='); out.append((char)encodingTable[ch >> 4]); out.append((char)encodingTable[ch & 0x0F]); count += 3; } else { // good character, just use unchanged. out.append((char)ch); count++; } } return count; } /** * Specialized version of the decoder that handles encoding of * RFC 2047 encoded word values. This has special handling for * certain characters, but less special handling for blanks and * linebreaks. * * @param ch * @param specials * * @exception IOException */ public void encode(int ch, String specials) throws IOException { // make sure this is just a single byte value. ch = ch &0xFF; // spaces require special handling. If the next character is a line terminator, then // the space needs to be encoded. if (ch == ' ') { // blanks get translated into underscores, because the encoded tokens can't have embedded blanks. writeCharacter('_'); } // non-ascii chars and the designated specials all get encoded. else if (ch < 32 || ch >= 127 || specials.indexOf(ch) != -1) { writeEncodedCharacter(ch); } else { // good character, just use unchanged. writeCharacter(ch); } } /** * encode the input data producing a UUEncoded output stream. * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * @param out The output stream the encoded data is written to. * * @return the number of bytes produced. */ public int encode(byte[] data, int off, int length, OutputStream out) throws IOException { // make sure we're writing to the correct stream this.out = out; bytesWritten = 0; // do the actual encoding return encode(data, off, length); } /** * decode the uuencoded byte data writing it to the given output stream * * @param data The array of byte data to decode. * @param off Starting offset within the array. * @param length The length of data to encode. * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. * @exception IOException */ public int decode(byte[] data, int off, int length, OutputStream out) throws IOException { // make sure we're writing to the correct stream this.out = out; int endOffset = off + length; int bytesWritten = 0; while (off < endOffset) { byte ch = data[off++]; // space characters are a pain. We need to scan ahead until we find a non-space character. // if the character is a line terminator, we need to discard the blanks. if (ch == ' ') { int trailingSpaces = 1; // scan forward, counting the characters. while (off < endOffset && data[off] == ' ') { // step forward and count this. off++; trailingSpaces++; } // is this a lineend at the current location? if (off >= endOffset || data[off] == '\r' || data[off] == '\n') { // go to the next one continue; } else { // make sure we account for the spaces in the output count. bytesWritten += trailingSpaces; // write out the blank characters we counted and continue with the non-blank. while (trailingSpaces-- > 0) { out.write(' '); } } } else if (ch == '=') { // we found an encoded character. Reduce the 3 char sequence to one. // but first, make sure we have two characters to work with. if (off + 1 >= endOffset) { throw new IOException("Invalid quoted printable encoding"); } // convert the two bytes back from hex. byte b1 = data[off++]; byte b2 = data[off++]; // we've found an encoded carriage return. The next char needs to be a newline if (b1 == '\r') { if (b2 != '\n') { throw new IOException("Invalid quoted printable encoding"); } // this was a soft linebreak inserted by the encoding. We just toss this away // on decode. } else { // this is a hex pair we need to convert back to a single byte. b1 = decodingTable[b1]; b2 = decodingTable[b2]; out.write((b1 << 4) | b2); // 3 bytes in, one byte out bytesWritten++; } } else { // simple character, just write it out. out.write(ch); bytesWritten++; } } return bytesWritten; } /** * Decode a byte array of data. * * @param data The data array. * @param out The output stream target for the decoded data. * * @return The number of bytes written to the stream. * @exception IOException */ public int decodeWord(byte[] data, OutputStream out) throws IOException { return decodeWord(data, 0, data.length, out); } /** * decode the uuencoded byte data writing it to the given output stream * * @param data The array of byte data to decode. * @param off Starting offset within the array. * @param length The length of data to encode. * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. * @exception IOException */ public int decodeWord(byte[] data, int off, int length, OutputStream out) throws IOException { // make sure we're writing to the correct stream this.out = out; int endOffset = off + length; int bytesWritten = 0; while (off < endOffset) { byte ch = data[off++]; // space characters were translated to '_' on encode, so we need to translate them back. if (ch == '_') { out.write(' '); } else if (ch == '=') { // we found an encoded character. Reduce the 3 char sequence to one. // but first, make sure we have two characters to work with. if (off + 1 >= endOffset) { throw new IOException("Invalid quoted printable encoding"); } // convert the two bytes back from hex. byte b1 = data[off++]; byte b2 = data[off++]; // we've found an encoded carriage return. The next char needs to be a newline if (b1 == '\r') { if (b2 != '\n') { throw new IOException("Invalid quoted printable encoding"); } // this was a soft linebreak inserted by the encoding. We just toss this away // on decode. } else { // this is a hex pair we need to convert back to a single byte. byte c1 = decodingTable[b1]; byte c2 = decodingTable[b2]; out.write((c1 << 4) | c2); // 3 bytes in, one byte out bytesWritten++; } } else { // simple character, just write it out. out.write(ch); bytesWritten++; } } return bytesWritten; } /** * decode the UUEncoded String data writing it to the given output stream. * * @param data The String data to decode. * @param out The output stream to write the decoded data to. * * @return the number of bytes produced. * @exception IOException */ public int decode(String data, OutputStream out) throws IOException { try { // just get the byte data and decode. byte[] bytes = data.getBytes("US-ASCII"); return decode(bytes, 0, bytes.length, out); } catch (UnsupportedEncodingException e) { throw new IOException("Invalid UUEncoding"); } } private void checkLineLength(int required) throws IOException { // if we're at our line length limit, write out a soft line break and reset. if ((lineCount + required) >= lineLength ) { out.write('='); out.write('\r'); out.write('\n'); bytesWritten += 3; lineCount = 0; } } public void writeEncodedCharacter(int ch) throws IOException { // we need 3 characters for an encoded value checkLineLength(3); out.write('='); out.write(encodingTable[ch >> 4]); out.write(encodingTable[ch & 0x0F]); lineCount += 3; bytesWritten += 3; } public void writeCharacter(int ch) throws IOException { // we need 3 characters for an encoded value checkLineLength(1); out.write(ch); lineCount++; bytesWritten++; } public void writeEOL() throws IOException { out.write('\r'); out.write('\n'); lineCount = 0; bytesWritten += 3; } public int decode(InputStream in) throws IOException { // we potentially need to scan over spans of whitespace characters to determine if they're real // we just return blanks until the count goes to zero. if (deferredWhitespace > 0) { deferredWhitespace--; return ' '; } // we may have needed to scan ahead to find the first non-blank character, which we would store here. // hand that back once we're done with the blanks. if (cachedCharacter != -1) { int result = cachedCharacter; cachedCharacter = -1; return result; } int ch = in.read(); // reflect back an EOF condition. if (ch == -1) { return -1; } // space characters are a pain. We need to scan ahead until we find a non-space character. // if the character is a line terminator, we need to discard the blanks. if (ch == ' ') { // scan forward, counting the characters. while ((ch = in.read()) == ' ') { deferredWhitespace++; } // is this a lineend at the current location? if (ch == -1 || ch == '\r' || ch == '\n') { // those blanks we so zealously counted up don't really exist. Clear out the counter. deferredWhitespace = 0; // return the real significant character now. return ch; } // remember this character for later, after we've used up the deferred blanks. cachedCharacter = decodeNonspaceChar(in, ch); // return this space. We did not include this one in the deferred count, so we're right in sync. return ' '; } return decodeNonspaceChar(in, ch); } private int decodeNonspaceChar(InputStream in, int ch) throws IOException { if (ch == '=') { int b1 = in.read(); // we need to get two characters after the quotation marker if (b1 == -1) { throw new IOException("Truncated quoted printable data"); } int b2 = in.read(); // we need to get two characters after the quotation marker if (b2 == -1) { throw new IOException("Truncated quoted printable data"); } // we've found an encoded carriage return. The next char needs to be a newline if (b1 == '\r') { if (b2 != '\n') { throw new IOException("Invalid quoted printable encoding"); } // this was a soft linebreak inserted by the encoding. We just toss this away // on decode. We need to return something, so recurse and decode the next. return decode(in); } else { // this is a hex pair we need to convert back to a single byte. b1 = decodingTable[b1]; b2 = decodingTable[b2]; return (b1 << 4) | b2; } } else { return ch; } } /** * Perform RFC-2047 word encoding using Q-P data encoding. * * @param in The source for the encoded data. * @param charset The charset tag to be added to each encoded data section. * @param specials The set of special characters that we require to encoded. * @param out The output stream where the encoded data is to be written. * @param fold Controls whether separate sections of encoded data are separated by * linebreaks or whitespace. * * @exception IOException */ public void encodeWord(InputStream in, String charset, String specials, OutputStream out, boolean fold) throws IOException { // we need to scan ahead in a few places, which may require pushing characters back on to the stream. // make sure we have a stream where this is possible. PushbackInputStream inStream = new PushbackInputStream(in); PrintStream writer = new PrintStream(out); // segments of encoded data are limited to 75 byes, including the control sections. int limit = 75 - 7 - charset.length(); boolean firstLine = true; StringBuffer encodedString = new StringBuffer(76); while (true) { // encode another segment of data. encode(inStream, encodedString, specials, limit); // nothing encoded means we've hit the end of the data. if (encodedString.length() == 0) { break; } // if we have more than one segment, we need to insert separators. Depending on whether folding // was requested, this is either a blank or a linebreak. if (!firstLine) { if (fold) { writer.print("\r\n"); } else { writer.print(" "); } } // add the encoded word header writer.print("=?"); writer.print(charset); writer.print("?Q?"); // the data writer.print(encodedString.toString()); // and the terminator mark writer.print("?="); writer.flush(); // we reset the string buffer and reuse it. encodedString.setLength(0); // we need a delimiter between sections from this point on. firstLine = false; } } /** * Perform RFC-2047 word encoding using Base64 data encoding. * * @param in The source for the encoded data. * @param charset The charset tag to be added to each encoded data section. * @param out The output stream where the encoded data is to be written. * @param fold Controls whether separate sections of encoded data are separated by * linebreaks or whitespace. * * @exception IOException */ public void encodeWord(byte[] data, StringBuffer out, String charset, String specials) throws IOException { // append the word header out.append("=?"); out.append(charset); out.append("?Q?"); // add on the encodeded data encodeWordData(data, out, specials); // the end of the encoding marker out.append("?="); } /** * Perform RFC-2047 word encoding using Q-P data encoding. * * @param in The source for the encoded data. * @param charset The charset tag to be added to each encoded data section. * @param specials The set of special characters that we require to encoded. * @param out The output stream where the encoded data is to be written. * @param fold Controls whether separate sections of encoded data are separated by * linebreaks or whitespace. * * @exception IOException */ public void encodeWordData(byte[] data, StringBuffer out, String specials) throws IOException { for (int i = 0; i < data.length; i++) { int ch = data[i] & 0xff; ; // spaces require special handling. If the next character is a line terminator, then // the space needs to be encoded. if (ch == ' ') { // blanks get translated into underscores, because the encoded tokens can't have embedded blanks. out.append('_'); } // non-ascii chars and the designated specials all get encoded. else if (ch < 32 || ch >= 127 || specials.indexOf(ch) != -1) { out.append('='); out.append((char)encodingTable[ch >> 4]); out.append((char)encodingTable[ch & 0x0F]); } else { // good character, just use unchanged. out.append((char)ch); } } } /** * Estimate the final encoded size of a segment of data. * This is used to ensure that the encoded blocks do * not get split across a unicode character boundary and * that the encoding will fit within the bounds of * a mail header line. * * @param data The data we're anticipating encoding. * * @return The size of the byte data in encoded form. */ public int estimateEncodedLength(byte[] data, String specials) { int count = 0; for (int i = 0; i < data.length; i++) { // make sure this is just a single byte value. int ch = data[i] & 0xff; // non-ascii chars and the designated specials all get encoded. if (ch < 32 || ch >= 127 || specials.indexOf(ch) != -1) { // Q encoding translates a single char into 3 characters count += 3; } else { // non-encoded character count++; } } return count; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/UUEncoder.java0000664000175000017500000002017710517560657030224 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; public class UUEncoder implements Encoder { // this is the maximum number of chars allowed per line, since we have to include a uuencoded length at // the start of each line. static private final int MAX_CHARS_PER_LINE = 45; public UUEncoder() { } /** * encode the input data producing a UUEncoded output stream. * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * @param out The output stream the encoded data is written to. * * @return the number of bytes produced. */ public int encode(byte[] data, int off, int length, OutputStream out) throws IOException { int byteCount = 0; while (true) { // keep writing complete lines until we've exhausted the data. if (length > MAX_CHARS_PER_LINE) { // encode another line and adjust the length and position byteCount += encodeLine(data, off, MAX_CHARS_PER_LINE, out); length -= MAX_CHARS_PER_LINE; off += MAX_CHARS_PER_LINE; } else { // last line. Encode the partial and quit byteCount += encodeLine(data, off, MAX_CHARS_PER_LINE, out); break; } } return byteCount; } /** * Encode a single line of data (less than or equal to 45 characters). * * @param data The array of byte data. * @param off The starting offset within the data. * @param length Length of the data to encode. * @param out The output stream the encoded data is written to. * * @return The number of bytes written to the output stream. * @exception IOException */ private int encodeLine(byte[] data, int offset, int length, OutputStream out) throws IOException { // write out the number of characters encoded in this line. out.write((byte)((length & 0x3F) + ' ')); byte a; byte b; byte c; // count the bytes written...we add 2, one for the length and 1 for the linend terminator. int bytesWritten = 2; for (int i = 0; i < length;) { // set the padding defauls b = 1; c = 1; // get the next 3 bytes (if we have them) a = data[offset + i++]; if (i < length) { b = data[offset + i++]; if (i < length) { c = data[offset + i++]; } } byte d1 = (byte)(((a >>> 2) & 0x3F) + ' '); byte d2 = (byte)(((( a << 4) & 0x30) | ((b >>> 4) & 0x0F)) + ' '); byte d3 = (byte)((((b << 2) & 0x3C) | ((c >>> 6) & 0x3)) + ' '); byte d4 = (byte)((c & 0x3F) + ' '); out.write(d1); out.write(d2); out.write(d3); out.write(d4); bytesWritten += 4; } // terminate with a linefeed alone out.write('\n'); return bytesWritten; } /** * decode the uuencoded byte data writing it to the given output stream * * @param data The array of byte data to decode. * @param off Starting offset within the array. * @param length The length of data to encode. * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. * @exception IOException */ public int decode(byte[] data, int off, int length, OutputStream out) throws IOException { int bytesWritten = 0; while (length > 0) { int lineOffset = off; // scan forward looking for a EOL terminator for the next line of data. while (length > 0 && data[off] != '\n') { off++; length--; } // go decode this line of data bytesWritten += decodeLine(data, lineOffset, off - lineOffset, out); // the offset was left pointing at the EOL character, so step over that one before // scanning again. off++; length--; } return bytesWritten; } /** * decode a single line of uuencoded byte data writing it to the given output stream * * @param data The array of byte data to decode. * @param off Starting offset within the array. * @param length The length of data to decode (length does NOT include the terminating new line). * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. * @exception IOException */ private int decodeLine(byte[] data, int off, int length, OutputStream out) throws IOException { int count = data[off++]; // obtain and validate the count if (count < ' ') { throw new IOException("Invalid UUEncode line length"); } count = (count - ' ') & 0x3F; // get the rounded count of characters that should have been used to encode this. The + 1 is for the // length encoded at the beginning int requiredLength = (((count * 8) + 5) / 6) + 1; if (length < requiredLength) { throw new IOException("UUEncoded data and length do not match"); } int bytesWritten = 0; // now decode the bytes. while (bytesWritten < count) { // even one byte of data requires two bytes to encode, so we should have that. byte a = (byte)((data[off++] - ' ') & 0x3F); byte b = (byte)((data[off++] - ' ') & 0x3F); byte c = 0; byte d = 0; // do the first byte byte first = (byte)(((a << 2) & 0xFC) | ((b >>> 4) & 3)); out.write(first); bytesWritten++; // still have more bytes to decode? do the second byte of the second. That requires // a third byte from the data. if (bytesWritten < count) { c = (byte)((data[off++] - ' ') & 0x3F); byte second = (byte)(((b << 4) & 0xF0) | ((c >>> 2) & 0x0F)); out.write(second); bytesWritten++; // need the third one? if (bytesWritten < count) { d = (byte)((data[off++] - ' ') & 0x3F); byte third = (byte)(((c << 6) & 0xC0) | (d & 0x3F)); out.write(third); bytesWritten++; } } } return bytesWritten; } /** * decode the UUEncoded String data writing it to the given output stream. * * @param data The String data to decode. * @param out The output stream to write the decoded data to. * * @return the number of bytes produced. * @exception IOException */ public int decode(String data, OutputStream out) throws IOException { try { // just get the byte data and decode. byte[] bytes = data.getBytes("US-ASCII"); return decode(bytes, 0, bytes.length, out); } catch (UnsupportedEncodingException e) { throw new IOException("Invalid UUEncoding"); } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/UUDecoderStream.java0000664000175000017500000002073010517560657031361 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; /** * An implementation of a FilterOutputStream that decodes the * stream data in UU encoding format. This version does the * decoding "on the fly" rather than decoding a single block of * data. Since this version is intended for use by the MimeUtilty class, * it also handles line breaks in the encoded data. */ public class UUDecoderStream extends FilterInputStream { // maximum number of chars that can appear in a single line protected static final int MAX_CHARS_PER_LINE = 45; // our decoder for processing the data protected UUEncoder decoder = new UUEncoder(); // a buffer for one decoding unit's worth of data (45 bytes). protected byte[] decodedChars; // count of characters in the buffer protected int decodedCount = 0; // index of the next decoded character protected int decodedIndex = 0; // indicates whether we've already processed the "begin" prefix. protected boolean beginRead = false; public UUDecoderStream(InputStream in) { super(in); } /** * Test for the existance of decoded characters in our buffer * of decoded data. * * @return True if we currently have buffered characters. */ private boolean dataAvailable() { return decodedCount != 0; } /** * Get the next buffered decoded character. * * @return The next decoded character in the buffer. */ private byte getBufferedChar() { decodedCount--; return decodedChars[decodedIndex++]; } /** * Decode a requested number of bytes of data into a buffer. * * @return true if we were able to obtain more data, false otherwise. */ private boolean decodeStreamData() throws IOException { decodedIndex = 0; // fill up a data buffer with input data return fillEncodedBuffer() != -1; } /** * Retrieve a single byte from the decoded characters buffer. * * @return The decoded character or -1 if there was an EOF condition. */ private int getByte() throws IOException { if (!dataAvailable()) { if (!decodeStreamData()) { return -1; } } decodedCount--; return decodedChars[decodedIndex++]; } private int getBytes(byte[] data, int offset, int length) throws IOException { int readCharacters = 0; while (length > 0) { // need data? Try to get some if (!dataAvailable()) { // if we can't get this, return a count of how much we did get (which may be -1). if (!decodeStreamData()) { return readCharacters > 0 ? readCharacters : -1; } } // now copy some of the data from the decoded buffer to the target buffer int copyCount = Math.min(decodedCount, length); System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount); decodedIndex += copyCount; decodedCount -= copyCount; offset += copyCount; length -= copyCount; readCharacters += copyCount; } return readCharacters; } /** * Verify that the first line of the buffer is a valid begin * marker. * * @exception IOException */ private void checkBegin() throws IOException { // we only do this the first time we're requested to read from the stream. if (beginRead) { return; } // we might have to skip over lines to reach the marker. If we hit the EOF without finding // the begin, that's an error. while (true) { String line = readLine(); if (line == null) { throw new IOException("Missing UUEncode begin command"); } // is this our begin? if (line.regionMatches(true, 0, "begin ", 0, 6)) { // This is the droid we're looking for..... beginRead = true; return; } } } /** * Read a line of data. Returns null if there is an EOF. * * @return The next line read from the stream. Returns null if we * hit the end of the stream. * @exception IOException */ protected String readLine() throws IOException { decodedIndex = 0; // get an accumulator for the data StringBuffer buffer = new StringBuffer(); // now process a character at a time. int ch = in.read(); while (ch != -1) { // a naked new line completes the line. if (ch == '\n') { break; } // a carriage return by itself is ignored...we're going to assume that this is followed // by a new line because we really don't have the capability of pushing this back . else if (ch == '\r') { ; } else { // add this to our buffer buffer.append((char)ch); } ch = in.read(); } // if we didn't get any data at all, return nothing if (ch == -1 && buffer.length() == 0) { return null; } // convert this into a string. return buffer.toString(); } /** * Fill our buffer of input characters for decoding from the * stream. This will attempt read a full buffer, but will * terminate on an EOF or read error. This will filter out * non-Base64 encoding chars and will only return a valid * multiple of 4 number of bytes. * * @return The count of characters read. */ private int fillEncodedBuffer() throws IOException { checkBegin(); // reset the buffer position decodedIndex = 0; while (true) { // we read these as character lines. We need to be looking for the "end" marker for the // end of the data. String line = readLine(); // this should NOT be happening.... if (line == null) { throw new IOException("Missing end in UUEncoded data"); } // Is this the end marker? EOF baby, EOF! if (line.equalsIgnoreCase("end")) { // this indicates we got nuttin' more to do. return -1; } ByteArrayOutputStream out = new ByteArrayOutputStream(MAX_CHARS_PER_LINE); byte [] lineBytes; try { lineBytes = line.getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { throw new IOException("Invalid UUEncoding"); } // decode this line decodedCount = decoder.decode(lineBytes, 0, lineBytes.length, out); // not just a zero-length line? if (decodedCount != 0) { // get the resulting byte array decodedChars = out.toByteArray(); return decodedCount; } } } // in order to function as a filter, these streams need to override the different // read() signature. public int read() throws IOException { return getByte(); } public int read(byte [] buffer, int offset, int length) throws IOException { return getBytes(buffer, offset, length); } public boolean markSupported() { return false; } public int available() throws IOException { return ((in.available() / 4) * 3) + decodedCount; } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/Base64.java0000664000175000017500000001243510700432002027365 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public class Base64 { private static final Encoder encoder = new Base64Encoder(); /** * encode the input data producing a base 64 encoded byte array. * * @return a byte array containing the base 64 encoded data. */ public static byte[] encode( byte[] data) { // just forward to the general array encoder. return encode(data, 0, data.length); } /** * encode the input data producing a base 64 encoded byte array. * * @param data The data array to encode. * @param offset The starting offset within the data array. * @param length The length of the data to encode. * * @return a byte array containing the base 64 encoded data. */ public static byte[] encode( byte[] data, int offset, int length) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.encode(data, 0, data.length, bOut); } catch (IOException e) { throw new RuntimeException("exception encoding base64 string: " + e); } return bOut.toByteArray(); } /** * Encode the byte data to base 64 writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * Encode the byte data to base 64 writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, int off, int length, OutputStream out) throws IOException { return encoder.encode(data, off, length, out); } /** * decode the base 64 encoded input data. It is assumed the input data is valid. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data) { // just decode the entire array of data. return decode(data, 0, data.length); } /** * decode the base 64 encoded input data. It is assumed the input data is valid. * * @param data The data array to decode. * @param offset The offset of the data array. * @param length The length of data to decode. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data, int offset, int length) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, offset, length, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding base64 string: " + e); } return bOut.toByteArray(); } /** * decode the base 64 encoded String data - whitespace will be ignored. * * @return a byte array representing the decoded data. */ public static byte[] decode( String data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding base64 string: " + e); } return bOut.toByteArray(); } /** * decode the base 64 encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public static int decode( String data, OutputStream out) throws IOException { return encoder.decode(data, out); } /** * decode the base 64 encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @param data The array data to decode. * @param out The output stream for the data. * * @return the number of bytes produced. * @exception IOException */ public static int decode(byte [] data, OutputStream out) throws IOException { return encoder.decode(data, 0, data.length, out); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/SessionUtil.java0000664000175000017500000001660210517560657030652 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.security.Security; import javax.mail.Session; /** * Simple utility class for managing session properties. */ public class SessionUtil { /** * Get a property associated with this mail session. Returns * the provided default if it doesn't exist. * * @param session The attached session. * @param name The name of the property. * * @return The property value (returns null if the property has not been set). */ static public String getProperty(Session session, String name) { // occasionally, we get called with a null session if an object is not attached to // a session. In that case, treat this like an unknown parameter. if (session == null) { return null; } return session.getProperty(name); } /** * Get a property associated with this mail session. Returns * the provided default if it doesn't exist. * * @param session The attached session. * @param name The name of the property. * @param defaultValue * The default value to return if the property doesn't exist. * * @return The property value (returns defaultValue if the property has not been set). */ static public String getProperty(Session session, String name, String defaultValue) { String result = getProperty(session, name); if (result == null) { return defaultValue; } return result; } /** * Process a session property as a boolean value, returning * either true or false. * * @param session The source session. * @param name * * @return True if the property value is "true". Returns false for any * other value (including null). */ static public boolean isPropertyTrue(Session session, String name) { String property = getProperty(session, name); if (property != null) { return property.equals("true"); } return false; } /** * Process a session property as a boolean value, returning * either true or false. * * @param session The source session. * @param name * * @return True if the property value is "false". Returns false for * other value (including null). */ static public boolean isPropertyFalse(Session session, String name) { String property = getProperty(session, name); if (property != null) { return property.equals("false"); } return false; } /** * Get a property associated with this mail session as an integer value. Returns * the default value if the property doesn't exist or it doesn't have a valid int value. * * @param session The source session. * @param name The name of the property. * @param defaultValue * The default value to return if the property doesn't exist. * * @return The property value converted to an int. */ static public int getIntProperty(Session session, String name, int defaultValue) { String result = getProperty(session, name); if (result != null) { try { // convert into an int value. return Integer.parseInt(result); } catch (NumberFormatException e) { } } // return default value if it doesn't exist is isn't convertable. return defaultValue; } /** * Get a property associated with this mail session as a boolean value. Returns * the default value if the property doesn't exist or it doesn't have a valid boolean value. * * @param session The source session. * @param name The name of the property. * @param defaultValue * The default value to return if the property doesn't exist. * * @return The property value converted to a boolean. */ static public boolean getBooleanProperty(Session session, String name, boolean defaultValue) { String result = getProperty(session, name); if (result != null) { return Boolean.valueOf(result).booleanValue(); } // return default value if it doesn't exist is isn't convertable. return defaultValue; } /** * Get a system property associated with this mail session as a boolean value. Returns * the default value if the property doesn't exist or it doesn't have a valid boolean value. * * @param name The name of the property. * @param defaultValue * The default value to return if the property doesn't exist. * * @return The property value converted to a boolean. */ static public boolean getBooleanProperty(String name, boolean defaultValue) { try { String result = System.getProperty(name); if (result != null) { return Boolean.valueOf(result).booleanValue(); } } catch (SecurityException e) { // we can't access the property, so for all intents, it doesn't exist. } // return default value if it doesn't exist is isn't convertable. return defaultValue; } /** * Get a system property associated with this mail session as a boolean value. Returns * the default value if the property doesn't exist. * * @param name The name of the property. * @param defaultValue * The default value to return if the property doesn't exist. * * @return The property value */ static public String getProperty(String name, String defaultValue) { try { String result = System.getProperty(name); if (result != null) { return result; } } catch (SecurityException e) { // we can't access the property, so for all intents, it doesn't exist. } // return default value if it doesn't exist is isn't convertable. return defaultValue; } /** * Get a system property associated with this mail session as a boolean value. Returns * the default value if the property doesn't exist. * * @param name The name of the property. * * @return The property value */ static public String getProperty(String name) { try { return System.getProperty(name); } catch (SecurityException e) { // we can't access the property, so for all intents, it doesn't exist. } // return null if we got an exception. return null; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/StringBufferOutputStream.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/StringBufferOutputStrea0000664000175000017500000000336010517560657032266 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.IOException; import java.io.OutputStream; /** * An implementation of an OutputStream that writes the data directly * out to a StringBuffer object. Useful for applications where an * intermediate ByteArrayOutputStream is required to append generated * characters to a StringBuffer; */ public class StringBufferOutputStream extends OutputStream { // the target buffer protected StringBuffer buffer; /** * Create an output stream that writes to the target StringBuffer * * @param out The wrapped output stream. */ public StringBufferOutputStream(StringBuffer out) { buffer = out; } // in order for this to work, we only need override the single character form, as the others // funnel through this one by default. public void write(int ch) throws IOException { // just append the character buffer.append((char)ch); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/util/XText.java0000664000175000017500000001043410517560657027442 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Encoder for RFC1891 xtext. * * xtext strings are defined as * * xtext = *( xchar / hexchar ) * * where * * xchar is any ASCII character in the range 33-126, EXCEPT * the characters "+" and "=". * * hexchar is an ASCII "+" followed by two upper case * hexadecimal digits. */ public class XText { private static final Encoder encoder = new XTextEncoder(); /** * encode the input data producing an xtext encoded byte array. * * @return a byte array containing the xtext encoded data. */ public static byte[] encode( byte[] data) { return encode(data, 0, data.length); } /** * encode the input data producing an xtext encoded byte array. * * @return a byte array containing the xtext encoded data. */ public static byte[] encode( byte[] data, int off, int length) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.encode(data, off, length, bOut); } catch (IOException e) { throw new RuntimeException("exception encoding xtext string: " + e); } return bOut.toByteArray(); } /** * xtext encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * extext encode the byte data writing it to the given output stream. * * @return the number of bytes produced. */ public static int encode( byte[] data, int off, int length, OutputStream out) throws IOException { return encoder.encode(data, 0, data.length, out); } /** * decode the xtext encoded input data. It is assumed the input data is valid. * * @return a byte array representing the decoded data. */ public static byte[] decode( byte[] data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, 0, data.length, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding xtext string: " + e); } return bOut.toByteArray(); } /** * decode the xtext encoded String data - whitespace will be ignored. * * @return a byte array representing the decoded data. */ public static byte[] decode( String data) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { encoder.decode(data, bOut); } catch (IOException e) { throw new RuntimeException("exception decoding xtext string: " + e); } return bOut.toByteArray(); } /** * decode the xtext encoded String data writing it to the given output stream, * whitespace characters will be ignored. * * @return the number of bytes produced. */ public static int decode( String data, OutputStream out) throws IOException { return encoder.decode(data, out); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/0000775000175000017500000000000011703375727026344 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java0000664000175000017500000000205610517560657031150 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.handlers; import javax.activation.ActivationDataFlavor; public class XMLHandler extends TextHandler { public XMLHandler() { super(new ActivationDataFlavor(java.lang.String.class, "text/xml", "XML String")); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java0000664000175000017500000000751611375023416032070 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.handlers; import javax.activation.ActivationDataFlavor; import javax.activation.DataContentHandler; import javax.activation.DataSource; import javax.mail.internet.ContentType; import javax.mail.Message; import javax.mail.MessageAware; import javax.mail.MessageContext; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import javax.mail.internet.ParseException; import java.awt.datatransfer.DataFlavor; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; public class MessageHandler implements DataContentHandler { /** * Field dataFlavor */ ActivationDataFlavor dataFlavor; public MessageHandler(){ dataFlavor = new ActivationDataFlavor(java.lang.String.class, "message/rfc822", "Text"); } /** * Method getDF * * @return dataflavor */ protected ActivationDataFlavor getDF() { return dataFlavor; } /** * Method getTransferDataFlavors * * @return dataflavors */ public DataFlavor[] getTransferDataFlavors() { return (new DataFlavor[]{dataFlavor}); } /** * Method getTransferData * * @param dataflavor * @param datasource * @return * @throws IOException */ public Object getTransferData(DataFlavor dataflavor, DataSource datasource) throws IOException { if (getDF().equals(dataflavor)) { return getContent(datasource); } return null; } /** * Method getContent * * @param datasource * @return * @throws IOException */ public Object getContent(DataSource datasource) throws IOException { try { // if this is a proper message, it implements the MessageAware interface. We need this to // get the associated session. if (datasource instanceof MessageAware) { MessageContext context = ((MessageAware)datasource).getMessageContext(); // construct a mime message instance from the stream, associating it with the // data source session. return new MimeMessage(context.getSession(), datasource.getInputStream()); } } catch (MessagingException e) { // we need to transform any exceptions into an IOException. throw new IOException("Exception writing MimeMultipart: " + e.toString()); } return null; } /** * Method writeTo * * @param object * @param s * @param outputstream * @throws IOException */ public void writeTo(Object object, String s, OutputStream outputstream) throws IOException { // proper message type? if (object instanceof Message) { try { ((Message)object).writeTo(outputstream); } catch (MessagingException e) { throw new IOException("Error parsing message: " + e.toString()); } } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.ja0000664000175000017500000000671110517560657032144 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.handlers; import javax.activation.ActivationDataFlavor; import javax.activation.DataContentHandler; import javax.activation.DataSource; import java.awt.datatransfer.DataFlavor; import java.io.IOException; import java.io.OutputStream; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeMessage; import javax.mail.MessagingException; public class MultipartHandler implements DataContentHandler { /** * Field dataFlavor */ ActivationDataFlavor dataFlavor; public MultipartHandler(){ dataFlavor = new ActivationDataFlavor(javax.mail.internet.MimeMultipart.class, "multipart/mixed", "Multipart"); } /** * Constructor TextHandler * * @param dataFlavor */ public MultipartHandler(ActivationDataFlavor dataFlavor) { this.dataFlavor = dataFlavor; } /** * Method getDF * * @return dataflavor */ protected ActivationDataFlavor getDF() { return dataFlavor; } /** * Method getTransferDataFlavors * * @return dataflavors */ public DataFlavor[] getTransferDataFlavors() { return (new DataFlavor[]{dataFlavor}); } /** * Method getTransferData * * @param dataflavor * @param datasource * @return * @throws IOException */ public Object getTransferData(DataFlavor dataflavor, DataSource datasource) throws IOException { if (getDF().equals(dataflavor)) { return getContent(datasource); } return null; } /** * Method getContent * * @param datasource * @return * @throws IOException */ public Object getContent(DataSource datasource) throws IOException { try { return new MimeMultipart(datasource); } catch (MessagingException e) { // if there is a syntax error from the datasource parsing, the content is // just null. return null; } } /** * Method writeTo * * @param object * @param s * @param outputstream * @throws IOException */ public void writeTo(Object object, String s, OutputStream outputstream) throws IOException { // if this object is a MimeMultipart, then delegate to the part. if (object instanceof MimeMultipart) { try { ((MimeMultipart)object).writeTo(outputstream); } catch (MessagingException e) { // we need to transform any exceptions into an IOException. throw new IOException("Exception writing MimeMultipart: " + e.toString()); } } } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java0000664000175000017500000000206310517560657031412 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.handlers; import javax.activation.ActivationDataFlavor; public class HtmlHandler extends TextHandler { public HtmlHandler() { super(new ActivationDataFlavor(java.lang.String.class, "text/html", "HTML String")); } } geronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java0000664000175000017500000001111511375023416031416 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail.handlers; import java.awt.datatransfer.DataFlavor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import javax.activation.ActivationDataFlavor; import javax.activation.DataContentHandler; import javax.activation.DataSource; import javax.mail.internet.ContentType; import javax.mail.internet.MimeUtility; import javax.mail.internet.ParseException; public class TextHandler implements DataContentHandler { /** * Field dataFlavor */ ActivationDataFlavor dataFlavor; public TextHandler(){ dataFlavor = new ActivationDataFlavor(java.lang.String.class, "text/plain", "Text String"); } /** * Constructor TextHandler * * @param dataFlavor */ public TextHandler(ActivationDataFlavor dataFlavor) { this.dataFlavor = dataFlavor; } /** * Method getDF * * @return dataflavor */ protected ActivationDataFlavor getDF() { return dataFlavor; } /** * Method getTransferDataFlavors * * @return dataflavors */ public DataFlavor[] getTransferDataFlavors() { return (new DataFlavor[]{dataFlavor}); } /** * Method getTransferData * * @param dataflavor * @param datasource * @return * @throws IOException */ public Object getTransferData(DataFlavor dataflavor, DataSource datasource) throws IOException { if (getDF().equals(dataflavor)) { return getContent(datasource); } return null; } /** * Method getContent * * @param datasource * @return * @throws IOException */ public Object getContent(DataSource datasource) throws IOException { InputStream is = datasource.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream(); int count; byte[] buffer = new byte[1000]; try { while ((count = is.read(buffer, 0, buffer.length)) > 0) { os.write(buffer, 0, count); } } finally { is.close(); } try { return os.toString(getCharSet(datasource.getContentType())); } catch (ParseException e) { throw new UnsupportedEncodingException(e.getMessage()); } } /** * Write an object of "our" type out to the provided * output stream. The content type might modify the * result based on the content type parameters. * * @param object The object to write. * @param contentType * The content mime type, including parameters. * @param outputstream * The target output stream. * * @throws IOException */ public void writeTo(Object object, String contentType, OutputStream outputstream) throws IOException { OutputStreamWriter os; try { String charset = getCharSet(contentType); os = new OutputStreamWriter(outputstream, charset); } catch (Exception ex) { throw new UnsupportedEncodingException(ex.toString()); } String content = (String) object; os.write(content, 0, content.length()); os.flush(); } /** * get the character set from content type * @param contentType * @return * @throws ParseException */ protected String getCharSet(String contentType) throws ParseException { ContentType type = new ContentType(contentType); String charset = type.getParameter("charset"); if (charset == null) { charset = "us-ascii"; } return MimeUtility.javaCharset(charset); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootgeronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/MailProviderBundleTrackerCustomizer.javageronimo-javamail-1.4-spec-1.7.1/src/main/java/org/apache/geronimo/mail/MailProviderBundleTrackerCus0000664000175000017500000000465111404403313032170 0ustar brianbrian/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.geronimo.mail; import java.net.URL; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import org.osgi.framework.Bundle; import org.osgi.framework.BundleEvent; import org.osgi.service.log.LogService; import org.osgi.util.tracker.BundleTrackerCustomizer; public class MailProviderBundleTrackerCustomizer implements BundleTrackerCustomizer { // our base Activator (used as a service source) private Activator activator; // the bundle hosting the activation code private Bundle activationBundle; public MailProviderBundleTrackerCustomizer(Activator a, Bundle b) { activator = a; activationBundle = b; } /** * Handle the activation of a new bundle. * * @param bundle The source bundle. * @param event The bundle event information. * * @return A return object. */ public Object addingBundle(Bundle bundle, BundleEvent event) { if (bundle.equals(activationBundle)) { return null; } return MailProviderRegistry.registerBundle(bundle); } public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) { // this will update for the new bundle MailProviderRegistry.registerBundle(bundle); } public void removedBundle(Bundle bundle, BundleEvent event, Object object) { MailProviderRegistry.unregisterBundle(bundle); } private void log(int level, String message) { activator.log(level, message); } private void log(int level, String message, Throwable th) { activator.log(level, message, th); } } geronimo-javamail-1.4-spec-1.7.1/src/test/0000775000175000017500000000000011703375727017105 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/resources/0000775000175000017500000000000011703375727021117 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/resources/wmtom.bin0000664000175000017500000000276610776731406022766 0ustar brianbrian----MIMEBoundary258DE2D105298B756D content-type:application/xop+xml; charset=utf-8; type="application/soap+xml" content-transfer-encoding:binary content-id:<0.15B50EF49317518B01@apache.org> http://localhost:8070/axis2/services/MTOMService/mtomSample ----MIMEBoundary258DE2D105298B756D content-id:<11.BBFC8D48A21258EBBD@apache.org> content-type:application/octet-stream content-transfer-encoding:binary saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda ----MIMEBoundary258DE2D105298B756D content-id:<2.CB365E36E21BD6491A@apache.org> content-type:application/octet-stream content-transfer-encoding:binary saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda saminda ----MIMEBoundary258DE2D105298B756D-- geronimo-javamail-1.4-spec-1.7.1/src/test/resources/test.dat0000664000175000017500000000010010455665653022562 0ustar brianbrian0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ geronimo-javamail-1.4-spec-1.7.1/src/test/java/0000775000175000017500000000000011703375727020026 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/0000775000175000017500000000000011703375727021137 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/0000775000175000017500000000000011703375727022061 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/SimpleFolder.java0000664000175000017500000001306210517560657025313 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class SimpleFolder extends Folder { private static final Message[] MESSAGE_ARRAY = new Message[0]; private List _messages = new LinkedList(); private String _name; public SimpleFolder(Store store) { this(store, "SimpleFolder"); } SimpleFolder(Store store, String name) { super(store); _name = name; } /* (non-Javadoc) * @see javax.mail.Folder#appendMessages(javax.mail.Message[]) */ public void appendMessages(Message[] messages) throws MessagingException { for (int i = 0; i < messages.length; i++) { Message message = messages[i]; _messages.add(message); } } /* (non-Javadoc) * @see javax.mail.Folder#close(boolean) */ public void close(boolean expunge) throws MessagingException { if (expunge) { expunge(); } } /* (non-Javadoc) * @see javax.mail.Folder#create(int) */ public boolean create(int type) throws MessagingException { if (type == HOLDS_MESSAGES) { return true; } else { throw new MessagingException("Cannot create folders that hold folders"); } } /* (non-Javadoc) * @see javax.mail.Folder#delete(boolean) */ public boolean delete(boolean recurse) throws MessagingException { _messages = new LinkedList(); return true; } /* (non-Javadoc) * @see javax.mail.Folder#exists() */ public boolean exists() throws MessagingException { return true; } /* (non-Javadoc) * @see javax.mail.Folder#expunge() */ public Message[] expunge() throws MessagingException { Iterator it = _messages.iterator(); List result = new LinkedList(); while (it.hasNext()) { Message message = (Message) it.next(); if (message.isSet(Flags.Flag.DELETED)) { it.remove(); result.add(message); } } // run through and renumber the messages for (int i = 0; i < _messages.size(); i++) { Message message = (Message) _messages.get(i); message.setMessageNumber(i); } return (Message[]) result.toArray(MESSAGE_ARRAY); } /* (non-Javadoc) * @see javax.mail.Folder#getFolder(java.lang.String) */ public Folder getFolder(String name) throws MessagingException { return null; } /* (non-Javadoc) * @see javax.mail.Folder#getFullName() */ public String getFullName() { return getName(); } /* (non-Javadoc) * @see javax.mail.Folder#getMessage(int) */ public Message getMessage(int id) throws MessagingException { return (Message) _messages.get(id); } /* (non-Javadoc) * @see javax.mail.Folder#getMessageCount() */ public int getMessageCount() throws MessagingException { return _messages.size(); } /* (non-Javadoc) * @see javax.mail.Folder#getName() */ public String getName() { return _name; } /* (non-Javadoc) * @see javax.mail.Folder#getParent() */ public Folder getParent() throws MessagingException { return null; } /* (non-Javadoc) * @see javax.mail.Folder#getPermanentFlags() */ public Flags getPermanentFlags() { return null; } /* (non-Javadoc) * @see javax.mail.Folder#getSeparator() */ public char getSeparator() throws MessagingException { return '/'; } /* (non-Javadoc) * @see javax.mail.Folder#getType() */ public int getType() throws MessagingException { return HOLDS_MESSAGES; } /* (non-Javadoc) * @see javax.mail.Folder#hasNewMessages() */ public boolean hasNewMessages() throws MessagingException { return false; } /* (non-Javadoc) * @see javax.mail.Folder#isOpen() */ public boolean isOpen() { return true; } /* (non-Javadoc) * @see javax.mail.Folder#list(java.lang.String) */ public Folder[] list(String pattern) throws MessagingException { return null; } /* (non-Javadoc) * @see javax.mail.Folder#open(int) */ public void open(int mode) throws MessagingException { if (mode != HOLDS_MESSAGES) { throw new MessagingException("SimpleFolder can only be opened with HOLDS_MESSAGES"); } } /* (non-Javadoc) * @see javax.mail.Folder#renameTo(javax.mail.Folder) */ public boolean renameTo(Folder newName) throws MessagingException { _name = newName.getName(); return true; } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/util/0000775000175000017500000000000011703375727023036 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/util/ByteArrayDataSourceTest.java0000664000175000017500000000455510517560657030427 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class ByteArrayDataSourceTest extends TestCase { public ByteArrayDataSourceTest(String arg0) { super(arg0); } public void testByteArray() throws Exception { doDataSourceTest(new ByteArrayDataSource("0123456789", "text/plain"), "text/plain"); doDataSourceTest(new ByteArrayDataSource("0123456789".getBytes(), "text/xml"), "text/xml"); ByteArrayInputStream in = new ByteArrayInputStream("0123456789".getBytes()); doDataSourceTest(new ByteArrayDataSource(in, "text/html"), "text/html"); try { ByteArrayDataSource source = new ByteArrayDataSource("01234567890", "text/plain"); source.getOutputStream(); fail(); } catch (IOException e) { } ByteArrayDataSource source = new ByteArrayDataSource("01234567890", "text/plain"); assertEquals(source.getName(), ""); source.setName("fred"); assertEquals(source.getName(), "fred"); } private void doDataSourceTest(ByteArrayDataSource source, String type) throws Exception { assertEquals(type, source.getContentType()); InputStream in = source.getInputStream(); byte[] bytes = new byte[10]; int count = in.read(bytes); assertEquals(count, bytes.length); assertEquals("0123456789", new String(bytes)); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/util/SharedFileInputStreamTest.java0000664000175000017500000001017610517560657030750 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.mail.internet.SharedInputStream; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class SharedFileInputStreamTest extends TestCase { File basedir = new File(System.getProperty("basedir", ".")); File testInput = new File(basedir, "src/test/resources/test.dat"); public SharedFileInputStreamTest(String arg0) { super(arg0); } public void testInput() throws Exception { doTestInput(new SharedFileInputStream(testInput)); doTestInput(new SharedFileInputStream(testInput.getPath())); doTestInput(new SharedFileInputStream(testInput, 16)); doTestInput(new SharedFileInputStream(testInput.getPath(), 16)); } public void doTestInput(SharedFileInputStream in) throws Exception { assertEquals(in.read(), '0'); assertEquals(in.getPosition(), 1); byte[] bytes = new byte[10]; assertEquals(in.read(bytes), 10); assertEquals(new String(bytes), "123456789a"); assertEquals(in.getPosition(), 11); assertEquals(in.read(bytes, 5, 5), 5); assertEquals(new String(bytes), "12345bcdef"); assertEquals(in.getPosition(), 16); assertEquals(in.skip(5), 5); assertEquals(in.getPosition(), 21); assertEquals(in.read(), 'l'); while (in.read() != '\n' ) { } assertEquals(in.read(), -1); in.close(); } public void testNewStream() throws Exception { SharedFileInputStream in = new SharedFileInputStream(testInput); SharedFileInputStream sub = (SharedFileInputStream)in.newStream(10, 10 + 26); assertEquals(sub.getPosition(), 0); assertEquals(in.read(), '0'); assertEquals(sub.read(), 'a'); sub.skip(1); assertEquals(sub.getPosition(), 2); while (sub.read() != 'z') { } assertEquals(sub.read(), -1); SharedFileInputStream sub2 = (SharedFileInputStream)sub.newStream(5, 10); sub.close(); // should not close in or sub2 assertEquals(sub2.getPosition(), 0); assertEquals(sub2.read(), 'f'); assertEquals(in.read(), '1'); // should still work sub2.close(); assertEquals(in.read(), '2'); // should still work in.close(); } public void testMark() throws Exception { doMarkTest(new SharedFileInputStream(testInput, 10)); SharedFileInputStream in = new SharedFileInputStream(testInput, 10); SharedFileInputStream sub = (SharedFileInputStream)in.newStream(5, -1); doMarkTest(sub); } private void doMarkTest(SharedFileInputStream in) throws Exception { assertTrue(in.markSupported()); byte[] buffer = new byte[60]; in.read(); in.read(); in.mark(50); int markSpot = in.read(); in.read(buffer, 0, 20); in.reset(); assertEquals(markSpot, in.read()); in.read(buffer, 0, 40); in.reset(); assertEquals(markSpot, in.read()); in.read(buffer, 0, 51); try { in.reset(); fail(); } catch (IOException e) { } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/util/SharedByteArrayInputStreamTest.java0000664000175000017500000000543310517560657031773 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.mail.internet.SharedInputStream; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class SharedByteArrayInputStreamTest extends TestCase { private String testString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private byte[] testData = testString.getBytes(); public SharedByteArrayInputStreamTest(String arg0) { super(arg0); } public void testInput() throws Exception { SharedByteArrayInputStream in = new SharedByteArrayInputStream(testData); assertEquals(in.read(), '0'); assertEquals(in.getPosition(), 1); byte[] bytes = new byte[10]; assertEquals(in.read(bytes), 10); assertEquals(new String(bytes), "123456789a"); assertEquals(in.getPosition(), 11); assertEquals(in.read(bytes, 5, 5), 5); assertEquals(new String(bytes), "12345bcdef"); assertEquals(in.getPosition(), 16); assertEquals(in.skip(5), 5); assertEquals(in.getPosition(), 21); assertEquals(in.read(), 'l'); while (in.read() != 'Z') { } assertEquals(in.read(), -1); } public void testNewStream() throws Exception { SharedByteArrayInputStream in = new SharedByteArrayInputStream(testData); SharedByteArrayInputStream sub = (SharedByteArrayInputStream)in.newStream(10, 10 + 26); assertEquals(sub.getPosition(), 0); assertEquals(in.read(), '0'); assertEquals(sub.read(), 'a'); sub.skip(1); assertEquals(sub.getPosition(), 2); while (sub.read() != 'z') { } assertEquals(sub.read(), -1); SharedByteArrayInputStream sub2 = (SharedByteArrayInputStream)sub.newStream(5, 10); assertEquals(sub2.getPosition(), 0); assertEquals(sub2.read(), 'f'); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/FlagsTest.java0000664000175000017500000001166410517560657024630 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class FlagsTest extends TestCase { private List flagtypes; private Flags flags; /** * Constructor for FlagsTest. * @param arg0 */ public FlagsTest(String name) { super(name); } /* * @see TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); flags = new Flags(); flagtypes = new LinkedList(); flagtypes.add(Flags.Flag.ANSWERED); flagtypes.add(Flags.Flag.DELETED); flagtypes.add(Flags.Flag.DRAFT); flagtypes.add(Flags.Flag.FLAGGED); flagtypes.add(Flags.Flag.RECENT); flagtypes.add(Flags.Flag.SEEN); Collections.shuffle(flagtypes); } public void testHashCode() { int before = flags.hashCode(); flags.add("Test"); assertTrue( "Before: " + before + ", now " + flags.hashCode(), flags.hashCode() != before); assertTrue(flags.hashCode() != 0); } /* * Test for void add(Flag) */ public void testAddAndRemoveFlag() { Iterator it = flagtypes.iterator(); while (it.hasNext()) { Flags.Flag flag = (Flags.Flag) it.next(); assertFalse(flags.contains(flag)); flags.add(flag); assertTrue(flags.contains(flag)); } it = flagtypes.iterator(); while (it.hasNext()) { Flags.Flag flag = (Flags.Flag) it.next(); flags.remove(flag); assertFalse(flags.contains(flag)); } } /* * Test for void add(String) */ public void testAddString() { assertFalse(flags.contains("Frog")); flags.add("Frog"); assertTrue(flags.contains("Frog")); flags.remove("Frog"); assertFalse(flags.contains("Frog")); } /* * Test for void add(Flags) */ public void testAddFlags() { Flags other = new Flags(); other.add("Stuff"); other.add(Flags.Flag.RECENT); flags.add(other); assertTrue(flags.contains("Stuff")); assertTrue(flags.contains(Flags.Flag.RECENT)); assertTrue(flags.contains(other)); assertTrue(flags.contains(flags)); flags.add("Thing"); assertTrue(flags.contains("Thing")); flags.remove(other); assertFalse(flags.contains("Stuff")); assertFalse(flags.contains(Flags.Flag.RECENT)); assertFalse(flags.contains(other)); assertTrue(flags.contains("Thing")); } /* * Test for boolean equals(Object) */ public void testEqualsObject() { Flags other = new Flags(); other.add("Stuff"); other.add(Flags.Flag.RECENT); flags.add(other); assertEquals(flags, other); } public void testGetSystemFlags() { flags.add("Stuff"); flags.add("Another"); flags.add(Flags.Flag.FLAGGED); flags.add(Flags.Flag.RECENT); Flags.Flag[] array = flags.getSystemFlags(); assertEquals(2, array.length); assertTrue( (array[0] == Flags.Flag.FLAGGED && array[1] == Flags.Flag.RECENT) || (array[0] == Flags.Flag.RECENT && array[1] == Flags.Flag.FLAGGED)); } public void testGetUserFlags() { final String stuff = "Stuff"; final String another = "Another"; flags.add(stuff); flags.add(another); flags.add(Flags.Flag.FLAGGED); flags.add(Flags.Flag.RECENT); String[] array = flags.getUserFlags(); assertEquals(2, array.length); assertTrue( (array[0] == stuff && array[1] == another) || (array[0] == another && array[1] == stuff)); } public void testClone() throws CloneNotSupportedException { flags.add("Thing"); flags.add(Flags.Flag.RECENT); Flags other = (Flags) flags.clone(); assertTrue(other != flags); assertEquals(other, flags); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/QuotaTest.java0000664000175000017500000000437310517560657024664 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import javax.mail.MessagingException; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class QuotaTest extends TestCase { public void testQuota() throws MessagingException { Quota quota = new Quota("Fred"); assertEquals(quota.quotaRoot, "Fred"); assertNull(quota.resources); quota.setResourceLimit("Storage", 20000); assertNotNull(quota.resources); assertTrue(quota.resources.length == 1); assertEquals(quota.resources[0].name, "Storage"); assertEquals(quota.resources[0].usage, 0); assertEquals(quota.resources[0].limit, 20000); quota.setResourceLimit("Storage", 30000); assertNotNull(quota.resources); assertTrue(quota.resources.length == 1); assertEquals(quota.resources[0].name, "Storage"); assertEquals(quota.resources[0].usage, 0); assertEquals(quota.resources[0].limit, 30000); quota.setResourceLimit("Folders", 5); assertNotNull(quota.resources); assertTrue(quota.resources.length == 2); assertEquals(quota.resources[0].name, "Storage"); assertEquals(quota.resources[0].usage, 0); assertEquals(quota.resources[0].limit, 30000); assertEquals(quota.resources[1].name, "Folders"); assertEquals(quota.resources[1].usage, 0); assertEquals(quota.resources[1].limit, 5); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/TestData.java0000664000175000017500000001042010517560657024432 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import javax.mail.internet.MimeMessage; public class TestData { public static Store getTestStore() { return new Store( getTestSession(), new URLName("http://alex@test.com")) { public Folder getDefaultFolder() throws MessagingException { return getTestFolder(); } public Folder getFolder(String name) throws MessagingException { if (name.equals("test")) { return getTestFolder(); } else { return null; } } public Folder getFolder(URLName name) throws MessagingException { return getTestFolder(); } }; } public static Session getTestSession() { return Session.getDefaultInstance(System.getProperties()); } public static Folder getTestFolder() { return new Folder(getTestStore()) { public void appendMessages(Message[] messages) throws MessagingException { } public void close(boolean expunge) throws MessagingException { } public boolean create(int type) throws MessagingException { return false; } public boolean delete(boolean recurse) throws MessagingException { return false; } public boolean exists() throws MessagingException { return false; } public Message[] expunge() throws MessagingException { return null; } public Folder getFolder(String name) throws MessagingException { return null; } public String getFullName() { return null; } public Message getMessage(int id) throws MessagingException { return null; } public int getMessageCount() throws MessagingException { return 0; } public String getName() { return null; } public Folder getParent() throws MessagingException { return null; } public Flags getPermanentFlags() { return null; } public char getSeparator() throws MessagingException { return 0; } public int getType() throws MessagingException { return 0; } public boolean hasNewMessages() throws MessagingException { return false; } public boolean isOpen() { return false; } public Folder[] list(String pattern) throws MessagingException { return null; } public void open(int mode) throws MessagingException { } public boolean renameTo(Folder newName) throws MessagingException { return false; } }; } public static Transport getTestTransport() { return new Transport( getTestSession(), new URLName("http://host.name")) { public void sendMessage(Message message, Address[] addresses) throws MessagingException { // TODO Auto-generated method stub } }; } public static Message getMessage() { return new MimeMessage(getTestFolder(), 1) { }; } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/SimpleTextMessage.java0000664000175000017500000002655110517560657026340 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import javax.activation.DataHandler; import javax.mail.internet.InternetAddress; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class SimpleTextMessage extends Message { public static final Address[] ADDRESS_ARRAY = new Address[0]; private List _bcc = new LinkedList(); private List _cc = new LinkedList(); private String _description; private Flags _flags = new Flags(); private List _from = new LinkedList(); private Date _received; private Date _sent; private String _subject; private String _text; private List _to = new LinkedList(); /** * @param folder * @param number */ public SimpleTextMessage(Folder folder, int number) { super(folder, number); } /* (non-Javadoc) * @see javax.mail.Message#addFrom(javax.mail.Address[]) */ public void addFrom(Address[] addresses) throws MessagingException { _from.addAll(Arrays.asList(addresses)); } /* (non-Javadoc) * @see javax.mail.Part#addHeader(java.lang.String, java.lang.String) */ public void addHeader(String name, String value) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Message#addRecipients(javax.mail.Message.RecipientType, javax.mail.Address[]) */ public void addRecipients(RecipientType type, Address[] addresses) throws MessagingException { getList(type).addAll(Arrays.asList(addresses)); } /* (non-Javadoc) * @see javax.mail.Part#getAllHeaders() */ public Enumeration getAllHeaders() throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#getContent() */ public Object getContent() throws IOException, MessagingException { return _text; } /* (non-Javadoc) * @see javax.mail.Part#getContentType() */ public String getContentType() throws MessagingException { return "text/plain"; } /* (non-Javadoc) * @see javax.mail.Part#getDataHandler() */ public DataHandler getDataHandler() throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#getDescription() */ public String getDescription() throws MessagingException { return _description; } /* (non-Javadoc) * @see javax.mail.Part#getDisposition() */ public String getDisposition() throws MessagingException { return Part.INLINE; } /* (non-Javadoc) * @see javax.mail.Part#getFileName() */ public String getFileName() throws MessagingException { return null; } /* (non-Javadoc) * @see javax.mail.Message#getFlags() */ public Flags getFlags() throws MessagingException { return _flags; } /* (non-Javadoc) * @see javax.mail.Message#getFrom() */ public Address[] getFrom() throws MessagingException { return (Address[]) _from.toArray(ADDRESS_ARRAY); } /* (non-Javadoc) * @see javax.mail.Part#getHeader(java.lang.String) */ public String[] getHeader(String name) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#getInputStream() */ public InputStream getInputStream() throws IOException, MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#getLineCount() */ public int getLineCount() throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } private List getList(RecipientType type) throws MessagingException { List list; if (type == RecipientType.TO) { list = _to; } else if (type == RecipientType.CC) { list = _cc; } else if (type == RecipientType.BCC) { list = _bcc; } else { throw new MessagingException("Address type not understood"); } return list; } /* (non-Javadoc) * @see javax.mail.Part#getMatchingHeaders(java.lang.String[]) */ public Enumeration getMatchingHeaders(String[] names) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#getNonMatchingHeaders(java.lang.String[]) */ public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Message#getReceivedDate() */ public Date getReceivedDate() throws MessagingException { return _received; } /* (non-Javadoc) * @see javax.mail.Message#getRecipients(javax.mail.Message.RecipientType) */ public Address[] getRecipients(RecipientType type) throws MessagingException { return (Address[]) getList(type).toArray(ADDRESS_ARRAY); } /* (non-Javadoc) * @see javax.mail.Message#getSentDate() */ public Date getSentDate() throws MessagingException { return _sent; } /* (non-Javadoc) * @see javax.mail.Part#getSize() */ public int getSize() throws MessagingException { return _text.length(); } /* (non-Javadoc) * @see javax.mail.Message#getSubject() */ public String getSubject() throws MessagingException { return _subject; } /* (non-Javadoc) * @see javax.mail.Part#isMimeType(java.lang.String) */ public boolean isMimeType(String mimeType) throws MessagingException { return mimeType.equals("text/plain") || mimeType.equals("text/*"); } /* (non-Javadoc) * @see javax.mail.Part#removeHeader(java.lang.String) */ public void removeHeader(String name) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Message#reply(boolean) */ public Message reply(boolean replyToAll) throws MessagingException { try { SimpleTextMessage reply = (SimpleTextMessage) this.clone(); reply._to = new LinkedList(_from); if (replyToAll) { reply._to.addAll(_cc); } return reply; } catch (CloneNotSupportedException e) { throw new MessagingException(e.getMessage()); } } /* (non-Javadoc) * @see javax.mail.Message#saveChanges() */ public void saveChanges() throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#setContent(javax.mail.Multipart) */ public void setContent(Multipart content) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#setContent(java.lang.Object, java.lang.String) */ public void setContent(Object content, String type) throws MessagingException { setText((String) content); } /* (non-Javadoc) * @see javax.mail.Part#setDataHandler(javax.activation.DataHandler) */ public void setDataHandler(DataHandler handler) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#setDescription(java.lang.String) */ public void setDescription(String description) throws MessagingException { _description = description; } /* (non-Javadoc) * @see javax.mail.Part#setDisposition(java.lang.String) */ public void setDisposition(String disposition) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Part#setFileName(java.lang.String) */ public void setFileName(String name) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Message#setFlags(javax.mail.Flags, boolean) */ public void setFlags(Flags flags, boolean set) throws MessagingException { if (set) { _flags.add(flags); } else { _flags.remove(flags); } } /* (non-Javadoc) * @see javax.mail.Message#setFrom() */ public void setFrom() throws MessagingException { setFrom(new InternetAddress("root@localhost")); } /* (non-Javadoc) * @see javax.mail.Message#setFrom(javax.mail.Address) */ public void setFrom(Address address) throws MessagingException { _from.clear(); _from.add(address); } /* (non-Javadoc) * @see javax.mail.Part#setHeader(java.lang.String, java.lang.String) */ public void setHeader(String name, String value) throws MessagingException { throw new UnsupportedOperationException("Method not implemented"); } /* (non-Javadoc) * @see javax.mail.Message#setRecipients(javax.mail.Message.RecipientType, javax.mail.Address[]) */ public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException { List list = getList(type); list.clear(); list.addAll(Arrays.asList(addresses)); } /* (non-Javadoc) * @see javax.mail.Message#setSentDate(java.util.Date) */ public void setSentDate(Date sent) throws MessagingException { _sent = sent; } /* (non-Javadoc) * @see javax.mail.Message#setSubject(java.lang.String) */ public void setSubject(String subject) throws MessagingException { _subject = subject; } /* (non-Javadoc) * @see javax.mail.Part#setText(java.lang.String) */ public void setText(String content) throws MessagingException { _text = content; } /* (non-Javadoc) * @see javax.mail.Part#writeTo(java.io.OutputStream) */ public void writeTo(OutputStream out) throws IOException, MessagingException { throw new UnsupportedOperationException("Method not implemented"); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/MessageContextTest.java0000664000175000017500000002316510517560657026524 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import javax.activation.DataHandler; import javax.mail.internet.MimeMessage; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class MessageContextTest extends TestCase { public void testNothing() { } /* public void testMessageContext() { Part p; MessageContext mc; p = new TestPart(); mc = new MessageContext(p); assertSame(p, mc.getPart()); assertNull(mc.getMessage()); assertNull(mc.getSession()); Session s = Session.getDefaultInstance(null); MimeMessage m = new MimeMessage(s); p = new TestMultipart(m); mc = new MessageContext(p); assertSame(p, mc.getPart()); assertSame(m,mc.getMessage()); assertSame(s,mc.getSession()); } private static class TestMultipart extends Multipart implements Part { public TestMultipart(Part p) { parent = p; } public void writeTo(OutputStream out) throws IOException, MessagingException { } public void addHeader(String name, String value) throws MessagingException { } public Enumeration getAllHeaders() throws MessagingException { return null; } public Object getContent() throws IOException, MessagingException { return null; } public DataHandler getDataHandler() throws MessagingException { return null; } public String getDescription() throws MessagingException { return null; } public String getDisposition() throws MessagingException { return null; } public String getFileName() throws MessagingException { return null; } public String[] getHeader(String name) throws MessagingException { return null; } public InputStream getInputStream() throws IOException, MessagingException { return null; } public int getLineCount() throws MessagingException { return 0; } public Enumeration getMatchingHeaders(String[] names) throws MessagingException { return null; } public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { return null; } public int getSize() throws MessagingException { return 0; } public boolean isMimeType(String mimeType) throws MessagingException { return false; } public void removeHeader(String name) throws MessagingException { } public void setContent(Multipart content) throws MessagingException { } public void setContent(Object content, String type) throws MessagingException { } public void setDataHandler(DataHandler handler) throws MessagingException { } public void setDescription(String description) throws MessagingException { } public void setDisposition(String disposition) throws MessagingException { } public void setFileName(String name) throws MessagingException { } public void setHeader(String name, String value) throws MessagingException { } public void setText(String content) throws MessagingException { } } private static class TestBodyPart extends BodyPart { public TestBodyPart(Multipart p) { super(); parent = p; } public void addHeader(String name, String value) throws MessagingException { } public Enumeration getAllHeaders() throws MessagingException { return null; } public Object getContent() throws IOException, MessagingException { return null; } public String getContentType() throws MessagingException { return null; } public DataHandler getDataHandler() throws MessagingException { return null; } public String getDescription() throws MessagingException { return null; } public String getDisposition() throws MessagingException { return null; } public String getFileName() throws MessagingException { return null; } public String[] getHeader(String name) throws MessagingException { return null; } public InputStream getInputStream() throws IOException, MessagingException { return null; } public int getLineCount() throws MessagingException { return 0; } public Enumeration getMatchingHeaders(String[] names) throws MessagingException { return null; } public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { return null; } public int getSize() throws MessagingException { return 0; } public boolean isMimeType(String mimeType) throws MessagingException { return false; } public void removeHeader(String name) throws MessagingException { } public void setContent(Multipart content) throws MessagingException { } public void setContent(Object content, String type) throws MessagingException { } public void setDataHandler(DataHandler handler) throws MessagingException { } public void setDescription(String description) throws MessagingException { } public void setDisposition(String disposition) throws MessagingException { } public void setFileName(String name) throws MessagingException { } public void setHeader(String name, String value) throws MessagingException { } public void setText(String content) throws MessagingException { } public void writeTo(OutputStream out) throws IOException, MessagingException { } } private static class TestPart implements Part { public void addHeader(String name, String value) throws MessagingException { } public Enumeration getAllHeaders() throws MessagingException { return null; } public Object getContent() throws IOException, MessagingException { return null; } public String getContentType() throws MessagingException { return null; } public DataHandler getDataHandler() throws MessagingException { return null; } public String getDescription() throws MessagingException { return null; } public String getDisposition() throws MessagingException { return null; } public String getFileName() throws MessagingException { return null; } public String[] getHeader(String name) throws MessagingException { return null; } public InputStream getInputStream() throws IOException, MessagingException { return null; } public int getLineCount() throws MessagingException { return 0; } public Enumeration getMatchingHeaders(String[] names) throws MessagingException { return null; } public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { return null; } public int getSize() throws MessagingException { return 0; } public boolean isMimeType(String mimeType) throws MessagingException { return false; } public void removeHeader(String name) throws MessagingException { } public void setContent(Multipart content) throws MessagingException { } public void setContent(Object content, String type) throws MessagingException { } public void setDataHandler(DataHandler handler) throws MessagingException { } public void setDescription(String description) throws MessagingException { } public void setDisposition(String disposition) throws MessagingException { } public void setFileName(String name) throws MessagingException { } public void setHeader(String name, String value) throws MessagingException { } public void setText(String content) throws MessagingException { } public void writeTo(OutputStream out) throws IOException, MessagingException { } } */ } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/0000775000175000017500000000000011703375727023711 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MimeMultipartTest.java0000664000175000017500000001536611375023416030206 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; import java.io.OutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.util.Properties; import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Message; import javax.activation.CommandMap; import javax.activation.MailcapCommandMap; import javax.activation.DataContentHandler; import javax.activation.DataSource; import javax.activation.DataHandler; import javax.activation.FileDataSource; import junit.framework.TestCase; /** * @version $Rev: 946312 $ $Date: 2010-05-19 13:59:42 -0400 (Wed, 19 May 2010) $ */ public class MimeMultipartTest extends TestCase { private CommandMap defaultMap; public void testWriteTo() throws MessagingException, IOException, Exception { writeToSetUp(); MimeMultipart mp = new MimeMultipart(); MimeBodyPart part1 = new MimeBodyPart(); part1.setHeader("foo", "bar"); part1.setContent("Hello World", "text/plain"); mp.addBodyPart(part1); MimeBodyPart part2 = new MimeBodyPart(); part2.setContent("Hello Again", "text/plain"); mp.addBodyPart(part2); mp.writeTo(System.out); writeToTearDown(); } public void testPreamble() throws MessagingException, IOException { Properties props = new Properties(); Session session = Session.getDefaultInstance(props); session.setDebug(true); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("rickmcg@gmail.com")); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("rick@us.ibm.com")); message.setSubject("test subject"); BodyPart messageBodyPart1 = new MimeBodyPart(); messageBodyPart1.setHeader("Content-Type", "text/xml"); messageBodyPart1.setHeader("Content-Transfer-Encoding", "binary"); messageBodyPart1.setText("This is a test"); MimeMultipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart1); multipart.setPreamble("This is a preamble"); assertEquals("This is a preamble", multipart.getPreamble()); message.setContent(multipart); ByteArrayOutputStream out = new ByteArrayOutputStream(); message.writeTo(out); out.writeTo(System.out); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); MimeMessage newMessage = new MimeMessage(session, in); assertEquals("This is a preamble\r\n", ((MimeMultipart)newMessage.getContent()).getPreamble()); } public void testMIMEWriting() throws IOException, MessagingException { File basedir = new File(System.getProperty("basedir", ".")); File testInput = new File(basedir, "src/test/resources/wmtom.bin"); FileInputStream inStream = new FileInputStream(testInput); Properties props = new Properties(); javax.mail.Session session = javax.mail.Session .getInstance(props, null); MimeMessage mimeMessage = new MimeMessage(session, inStream); DataHandler dh = mimeMessage.getDataHandler(); MimeMultipart multiPart = new MimeMultipart(dh.getDataSource()); MimeBodyPart mimeBodyPart0 = (MimeBodyPart) multiPart.getBodyPart(0); Object object0 = mimeBodyPart0.getContent(); assertNotNull(object0); MimeBodyPart mimeBodyPart1 = (MimeBodyPart) multiPart.getBodyPart(1); Object object1 = mimeBodyPart1.getContent(); assertNotNull(object1); assertEquals(multiPart.getCount(), 2); } protected void writeToSetUp() throws Exception { defaultMap = CommandMap.getDefaultCommandMap(); MailcapCommandMap myMap = new MailcapCommandMap(); myMap.addMailcap("text/plain;; x-java-content-handler=" + DummyTextHandler.class.getName()); myMap.addMailcap("multipart/*;; x-java-content-handler=" + DummyMultipartHandler.class.getName()); CommandMap.setDefaultCommandMap(myMap); } protected void writeToTearDown() throws Exception { CommandMap.setDefaultCommandMap(defaultMap); } public static class DummyTextHandler implements DataContentHandler { public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; //To change body of implemented methods use File | Settings | File Templates. } public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedFlavorException, IOException { return null; //To change body of implemented methods use File | Settings | File Templates. } public Object getContent(DataSource ds) throws IOException { return null; //To change body of implemented methods use File | Settings | File Templates. } public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException { os.write(((String)obj).getBytes("ISO8859-1")); } } public static class DummyMultipartHandler implements DataContentHandler { public DataFlavor[] getTransferDataFlavors() { throw new UnsupportedOperationException(); } public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedFlavorException, IOException { throw new UnsupportedOperationException(); } public Object getContent(DataSource ds) throws IOException { throw new UnsupportedOperationException(); } public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException { MimeMultipart mp = (MimeMultipart) obj; try { mp.writeTo(os); } catch (MessagingException e) { throw (IOException) new IOException(e.getMessage()).initCause(e); } } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/PreencodedMimeBodyPartTest.java0000664000175000017500000000541310517560657031744 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import javax.mail.MessagingException; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class PreencodedMimeBodyPartTest extends TestCase { public void testEncoding() throws Exception { PreencodedMimeBodyPart part = new PreencodedMimeBodyPart("base64"); assertEquals("base64", part.getEncoding()); } public void testUpdateHeaders() throws Exception { TestBodyPart part = new TestBodyPart("base64"); part.updateHeaders(); assertEquals("base64", part.getHeader("Content-Transfer-Encoding", null)); } public void testWriteTo() throws Exception { PreencodedMimeBodyPart part = new PreencodedMimeBodyPart("binary"); byte[] content = new byte[] { 81, 82, 83, 84, 85, 86 }; part.setContent(new String(content, "UTF-8"), "text/plain; charset=\"UTF-8\""); ByteArrayOutputStream out = new ByteArrayOutputStream(); part.writeTo(out); byte[] data = out.toByteArray(); // we need to scan forward to the actual content and verify it has been written without additional // encoding. Our marker is a "crlfcrlf" sequence. for (int i = 0; i < data.length; i++) { if (data[i] == '\r') { if (data[i + 1] == '\n' && data[i + 2] == '\r' && data[i + 3] == '\n') { for (int j = 0; j < content.length; j++) { assertEquals(data[i + 4 + j], content[j]); } } } } } public class TestBodyPart extends PreencodedMimeBodyPart { public TestBodyPart(String encoding) { super(encoding); } public void updateHeaders() throws MessagingException { super.updateHeaders(); } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/ContentTypeTest.java0000664000175000017500000001454411124341735027665 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.TestCase; /** * @version $Rev: 729233 $ $Date: 2008-12-24 00:08:45 -0500 (Wed, 24 Dec 2008) $ */ public class ContentTypeTest extends TestCase { public ContentTypeTest(String arg0) { super(arg0); } public void testContentType() throws ParseException { ContentType type = new ContentType(); assertNull(type.getPrimaryType()); assertNull(type.getSubType()); assertNull(type.getParameter("charset")); } public void testContentTypeStringStringParameterList() throws ParseException { ContentType type; ParameterList list = new ParameterList(";charset=us-ascii"); type = new ContentType("text", "plain", list); assertEquals("text", type.getPrimaryType()); assertEquals("plain", type.getSubType()); assertEquals("text/plain", type.getBaseType()); ParameterList parameterList = type.getParameterList(); assertEquals("us-ascii", parameterList.get("charset")); assertEquals("us-ascii", type.getParameter("charset")); } public void testContentTypeString() throws ParseException { ContentType type; type = new ContentType("text/plain"); assertEquals("text", type.getPrimaryType()); assertEquals("plain", type.getSubType()); assertEquals("text/plain", type.getBaseType()); assertNotNull(type.getParameterList()); assertNull(type.getParameter("charset")); type = new ContentType("image/audio;charset=us-ascii"); ParameterList parameterList = type.getParameterList(); assertEquals("image", type.getPrimaryType()); assertEquals("audio", type.getSubType()); assertEquals("image/audio", type.getBaseType()); assertEquals("us-ascii", parameterList.get("charset")); assertEquals("us-ascii", type.getParameter("charset")); } public void testGetPrimaryType() throws ParseException { } public void testGetSubType() throws ParseException { } public void testGetBaseType() throws ParseException { } public void testGetParameter() throws ParseException { } public void testGetParameterList() throws ParseException { } public void testSetPrimaryType() throws ParseException { ContentType type = new ContentType("text/plain"); type.setPrimaryType("binary"); assertEquals("binary", type.getPrimaryType()); assertEquals("plain", type.getSubType()); assertEquals("binary/plain", type.getBaseType()); } public void testSetSubType() throws ParseException { ContentType type = new ContentType("text/plain"); type.setSubType("html"); assertEquals("text", type.getPrimaryType()); assertEquals("html", type.getSubType()); assertEquals("text/html", type.getBaseType()); } public void testSetParameter() throws ParseException { } public void testSetParameterList() throws ParseException { } public void testToString() throws ParseException { ContentType type = new ContentType("text/plain"); assertEquals("text/plain", type.toString()); type.setParameter("foo", "bar"); assertEquals("text/plain; foo=bar", type.toString()); type.setParameter("bar", "me@apache.org"); assertEquals("text/plain; foo=bar; bar=\"me@apache.org\"", type.toString()); } public void testMatchContentType() throws ParseException { ContentType type = new ContentType("text/plain"); ContentType test = new ContentType("text/plain"); assertTrue(type.match(test)); test = new ContentType("TEXT/plain"); assertTrue(type.match(test)); assertTrue(test.match(type)); test = new ContentType("text/PLAIN"); assertTrue(type.match(test)); assertTrue(test.match(type)); test = new ContentType("text/*"); assertTrue(type.match(test)); assertTrue(test.match(type)); test = new ContentType("text/xml"); assertFalse(type.match(test)); assertFalse(test.match(type)); test = new ContentType("binary/plain"); assertFalse(type.match(test)); assertFalse(test.match(type)); test = new ContentType("*/plain"); assertFalse(type.match(test)); assertFalse(test.match(type)); } public void testMatchString() throws ParseException { ContentType type = new ContentType("text/plain"); assertTrue(type.match("text/plain")); assertTrue(type.match("TEXT/plain")); assertTrue(type.match("text/PLAIN")); assertTrue(type.match("TEXT/PLAIN")); assertTrue(type.match("TEXT/*")); assertFalse(type.match("text/xml")); assertFalse(type.match("binary/plain")); assertFalse(type.match("*/plain")); assertFalse(type.match("")); assertFalse(type.match("text/plain/yada")); } public void testSOAP12ContentType() throws ParseException { ContentType type = new ContentType("multipart/related; type=\"application/xop+xml\"; start=\"\"; start-info=\"application/soap+xml; action=\\\"urn:upload\\\"\"; boundary=\"----=_Part_10_5804917.1223557742343\""); assertEquals("multipart/related", type.getBaseType()); assertEquals("application/xop+xml", type.getParameter("type")); assertEquals("", type.getParameter("start")); assertEquals("application/soap+xml; action=\"urn:upload\"", type.getParameter("start-info")); assertEquals("----=_Part_10_5804917.1223557742343", type.getParameter("boundary")); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MailDateFormatTest.java0000664000175000017500000001067010755267416030252 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.SimpleTimeZone; import junit.framework.TestCase; /** * @version $Rev: 628009 $ $Date: 2008-02-15 05:53:02 -0500 (Fri, 15 Feb 2008) $ */ public class MailDateFormatTest extends TestCase { public void testMailDateFormat() throws ParseException { MailDateFormat mdf = new MailDateFormat(); Date date = mdf.parse("Wed, 27 Aug 2003 13:43:38 +0100 (BST)"); // don't we just love the Date class? Calendar cal = Calendar.getInstance(new SimpleTimeZone(+1 * 60 * 60 * 1000, "BST"), Locale.getDefault()); cal.setTime(date); assertEquals(2003, cal.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, cal.get(Calendar.MONTH)); assertEquals(27, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(Calendar.WEDNESDAY, cal.get(Calendar.DAY_OF_WEEK)); assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(43, cal.get(Calendar.MINUTE)); assertEquals(38, cal.get(Calendar.SECOND)); date = mdf.parse("Wed, 27-Aug-2003 13:43:38 +0100"); // don't we just love the Date class? cal = Calendar.getInstance(new SimpleTimeZone(+1 * 60 * 60 * 1000, "BST"), Locale.getDefault()); cal.setTime(date); assertEquals(2003, cal.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, cal.get(Calendar.MONTH)); assertEquals(27, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(Calendar.WEDNESDAY, cal.get(Calendar.DAY_OF_WEEK)); assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(43, cal.get(Calendar.MINUTE)); assertEquals(38, cal.get(Calendar.SECOND)); date = mdf.parse("27-Aug-2003 13:43:38 EST"); // don't we just love the Date class? cal = Calendar.getInstance(new SimpleTimeZone(-5 * 60 * 60 * 1000, "EST"), Locale.getDefault()); cal.setTime(date); assertEquals(2003, cal.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, cal.get(Calendar.MONTH)); assertEquals(27, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(Calendar.WEDNESDAY, cal.get(Calendar.DAY_OF_WEEK)); assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(43, cal.get(Calendar.MINUTE)); assertEquals(38, cal.get(Calendar.SECOND)); date = mdf.parse("27 Aug 2003 13:43 EST"); // don't we just love the Date class? cal = Calendar.getInstance(new SimpleTimeZone(-5 * 60 * 60 * 1000, "EST"), Locale.getDefault()); cal.setTime(date); assertEquals(2003, cal.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, cal.get(Calendar.MONTH)); assertEquals(27, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(Calendar.WEDNESDAY, cal.get(Calendar.DAY_OF_WEEK)); assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(43, cal.get(Calendar.MINUTE)); assertEquals(00, cal.get(Calendar.SECOND)); date = mdf.parse("27 Aug 03 13:43 EST"); // don't we just love the Date class? cal = Calendar.getInstance(new SimpleTimeZone(-5 * 60 * 60 * 1000, "EST"), Locale.getDefault()); cal.setTime(date); assertEquals(2003, cal.get(Calendar.YEAR)); assertEquals(Calendar.AUGUST, cal.get(Calendar.MONTH)); assertEquals(27, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(Calendar.WEDNESDAY, cal.get(Calendar.DAY_OF_WEEK)); assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(43, cal.get(Calendar.MINUTE)); assertEquals(00, cal.get(Calendar.SECOND)); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MimeTest.java0000664000175000017500000001230011404403313026254 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.Session; import junit.framework.TestCase; import org.apache.geronimo.mail.util.Base64; public class MimeTest extends TestCase { public void testWriteRead() throws Exception { System.setProperty("mail.mime.decodefilename", "true"); Session session = Session.getDefaultInstance(new Properties(), null); MimeMessage mime = new MimeMessage(session); MimeMultipart parts = new MimeMultipart("related; type=\"text/xml\"; start=\"\""); MimeBodyPart xmlPart = new MimeBodyPart(); xmlPart.setContentID(""); xmlPart.setDataHandler(new DataHandler(new ByteArrayDataSource("".getBytes(), "text/xml"))); parts.addBodyPart(xmlPart); MimeBodyPart jpegPart = new MimeBodyPart(); jpegPart.setContentID(""); String filename = "filename"; String encodedFilename = "=?UTF-8?B?" + new String(Base64.encode(filename.getBytes()), "ISO8859-1") + "?="; jpegPart.setFileName(encodedFilename); jpegPart.setDataHandler(new DataHandler(new ByteArrayDataSource(new byte[] { 0, 1, 2, 3, 4, 5 }, "image/jpeg"))); parts.addBodyPart(jpegPart); mime.setContent(parts); mime.setHeader("Content-Type", parts.getContentType()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mime.writeTo(baos); MimeMessage mime2 = new MimeMessage(session, new ByteArrayInputStream(baos.toByteArray())); assertTrue(mime2.getContent() instanceof MimeMultipart); MimeMultipart parts2 = (MimeMultipart) mime2.getContent(); assertEquals(mime.getContentType(), mime2.getContentType()); assertEquals(parts.getCount(), parts2.getCount()); assertTrue(parts2.getBodyPart(0) instanceof MimeBodyPart); assertTrue(parts2.getBodyPart(1) instanceof MimeBodyPart); MimeBodyPart xmlPart2 = (MimeBodyPart) parts2.getBodyPart(0); assertEquals(xmlPart.getContentID(), xmlPart2.getContentID()); ByteArrayOutputStream xmlBaos = new ByteArrayOutputStream(); copyInputStream(xmlPart.getDataHandler().getInputStream(), xmlBaos); ByteArrayOutputStream xmlBaos2 = new ByteArrayOutputStream(); copyInputStream(xmlPart2.getDataHandler().getInputStream(), xmlBaos2); assertEquals(xmlBaos.toString(), xmlBaos2.toString()); MimeBodyPart jpegPart2 = (MimeBodyPart) parts2.getBodyPart(1); assertEquals(jpegPart.getContentID(), jpegPart2.getContentID()); assertEquals(jpegPart.getFileName(), jpegPart2.getDataHandler().getName()); assertEquals(filename, jpegPart2.getDataHandler().getName()); ByteArrayOutputStream jpegBaos = new ByteArrayOutputStream(); copyInputStream(jpegPart.getDataHandler().getInputStream(), jpegBaos); ByteArrayOutputStream jpegBaos2 = new ByteArrayOutputStream(); copyInputStream(jpegPart2.getDataHandler().getInputStream(), jpegBaos2); assertEquals(jpegBaos.toString(), jpegBaos2.toString()); } public static class ByteArrayDataSource implements DataSource { private byte[] data; private String type; private String name = "unused"; public ByteArrayDataSource(byte[] data, String type) { this.data = data; this.type = type; } public InputStream getInputStream() throws IOException { if (data == null) throw new IOException("no data"); return new ByteArrayInputStream(data); } public OutputStream getOutputStream() throws IOException { throw new IOException("getOutputStream() not supported"); } public String getContentType() { return type; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public static void copyInputStream(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) >= 0) { out.write(buffer, 0, len); } in.close(); out.close(); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/InternetHeadersTest.java0000664000175000017500000000303310517560657030477 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayInputStream; import javax.mail.MessagingException; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class InternetHeadersTest extends TestCase { private InternetHeaders headers; public void testLoadSingleHeader() throws MessagingException { String stream = "content-type: text/plain\r\n\r\n"; headers.load(new ByteArrayInputStream(stream.getBytes())); String[] header = headers.getHeader("content-type"); assertNotNull(header); assertEquals("text/plain", header[0]); } protected void setUp() throws Exception { headers = new InternetHeaders(); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/AllInternetTests.java0000664000175000017500000000256210517560657030025 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.Test; import junit.framework.TestSuite; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class AllInternetTests { public static Test suite() { TestSuite suite = new TestSuite("Test for javax.mail.internet"); //$JUnit-BEGIN$ suite.addTest(new TestSuite(ContentTypeTest.class)); suite.addTest(new TestSuite(ParameterListTest.class)); suite.addTest(new TestSuite(InternetAddressTest.class)); //$JUnit-END$ return suite; } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MimeBodyPartTest.java0000664000175000017500000001551210517560657027754 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.UnsupportedEncodingException; import javax.mail.MessagingException; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class MimeBodyPartTest extends TestCase { File basedir = new File(System.getProperty("basedir", ".")); File testInput = new File(basedir, "src/test/resources/test.dat"); public void testGetSize() throws MessagingException { MimeBodyPart part = new MimeBodyPart(); assertEquals(part.getSize(), -1); part = new MimeBodyPart(new InternetHeaders(), new byte[] {'a', 'b', 'c'}); assertEquals(part.getSize(), 3); } public void testGetLineCount() throws MessagingException { MimeBodyPart part = new MimeBodyPart(); assertEquals(part.getLineCount(), -1); part = new MimeBodyPart(new InternetHeaders(), new byte[] {'a', 'b', 'c'}); assertEquals(part.getLineCount(), -1); } public void testGetContentType() throws MessagingException { MimeBodyPart part = new MimeBodyPart(); assertEquals(part.getContentType(), "text/plain"); part.setHeader("Content-Type", "text/xml"); assertEquals(part.getContentType(), "text/xml"); part = new MimeBodyPart(); part.setText("abc"); assertEquals(part.getContentType(), "text/plain"); } public void testIsMimeType() throws MessagingException { MimeBodyPart part = new MimeBodyPart(); assertTrue(part.isMimeType("text/plain")); assertTrue(part.isMimeType("text/*")); part.setHeader("Content-Type", "text/xml"); assertTrue(part.isMimeType("text/xml")); assertTrue(part.isMimeType("text/*")); } public void testGetDisposition() throws MessagingException { MimeBodyPart part = new MimeBodyPart(); assertNull(part.getDisposition()); part.setDisposition("inline"); assertEquals(part.getDisposition(), "inline"); } public void testSetDescription() throws MessagingException, UnsupportedEncodingException { MimeBodyPart part = new MimeBodyPart(); String simpleSubject = "Yada, yada"; String complexSubject = "Yada, yada\u0081"; String mungedSubject = "Yada, yada\u003F"; part.setDescription(simpleSubject); assertEquals(part.getDescription(), simpleSubject); part.setDescription(complexSubject, "UTF-8"); assertEquals(part.getDescription(), complexSubject); assertEquals(part.getHeader("Content-Description", null), MimeUtility.encodeText(complexSubject, "UTF-8", null)); part.setDescription(null); assertNull(part.getDescription()); } public void testSetFileName() throws Exception { MimeBodyPart part = new MimeBodyPart(); part.setFileName("test.dat"); assertEquals("test.dat", part.getFileName()); ContentDisposition disp = new ContentDisposition(part.getHeader("Content-Disposition", null)); assertEquals("test.dat", disp.getParameter("filename")); ContentType type = new ContentType(part.getHeader("Content-Type", null)); assertEquals("test.dat", type.getParameter("name")); MimeBodyPart part2 = new MimeBodyPart(); part2.setHeader("Content-Type", type.toString()); assertEquals("test.dat", part2.getFileName()); part2.setHeader("Content-Type", null); part2.setHeader("Content-Disposition", disp.toString()); assertEquals("test.dat", part2.getFileName()); } public void testAttachments() throws Exception { MimeBodyPart part = new MimeBodyPart(); byte[] testData = getFileData(testInput); part.attachFile(testInput); assertEquals(part.getFileName(), testInput.getName()); part.updateHeaders(); File temp1 = File.createTempFile("MIME", ".dat"); temp1.deleteOnExit(); part.saveFile(temp1); byte[] tempData = getFileData(temp1); compareFileData(testData, tempData); ByteArrayOutputStream out = new ByteArrayOutputStream(); part.writeTo(out); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); MimeBodyPart part2 = new MimeBodyPart(in); temp1 = File.createTempFile("MIME", ".dat"); temp1.deleteOnExit(); part2.saveFile(temp1); tempData = getFileData(temp1); compareFileData(testData, tempData); part = new MimeBodyPart(); part.attachFile(testInput.getPath()); assertEquals(part.getFileName(), testInput.getName()); part.updateHeaders(); temp1 = File.createTempFile("MIME", ".dat"); temp1.deleteOnExit(); part.saveFile(temp1.getPath()); tempData = getFileData(temp1); compareFileData(testData, tempData); out = new ByteArrayOutputStream(); part.writeTo(out); in = new ByteArrayInputStream(out.toByteArray()); part2 = new MimeBodyPart(in); temp1 = File.createTempFile("MIME", ".dat"); temp1.deleteOnExit(); part2.saveFile(temp1.getPath()); tempData = getFileData(temp1); compareFileData(testData, tempData); } private byte[] getFileData(File source) throws Exception { FileInputStream testIn = new FileInputStream(source); byte[] testData = new byte[(int)source.length()]; testIn.read(testData); testIn.close(); return testData; } private void compareFileData(byte[] file1, byte [] file2) { assertEquals(file1.length, file2.length); for (int i = 0; i < file1.length; i++) { assertEquals(file1[i], file2[i]); } } class TestMimeBodyPart extends MimeBodyPart { public TestMimeBodyPart() { super(); } public void updateHeaders() throws MessagingException { super.updateHeaders(); } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MimeMessageTest.java0000664000175000017500000003337310754633212027610 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Properties; import javax.activation.CommandMap; import javax.activation.MailcapCommandMap; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.swing.text.AbstractDocument.Content; import junit.framework.TestCase; /** * @version $Rev: 627556 $ $Date: 2008-02-13 13:27:22 -0500 (Wed, 13 Feb 2008) $ */ public class MimeMessageTest extends TestCase { private CommandMap defaultMap; private Session session; public void testWriteTo() throws MessagingException, IOException { MimeMessage msg = new MimeMessage(session); msg.setSender(new InternetAddress("foo")); msg.setHeader("foo", "bar"); MimeMultipart mp = new MimeMultipart(); MimeBodyPart part1 = new MimeBodyPart(); part1.setHeader("foo", "bar"); part1.setContent("Hello World", "text/plain"); mp.addBodyPart(part1); MimeBodyPart part2 = new MimeBodyPart(); part2.setContent("Hello Again", "text/plain"); mp.addBodyPart(part2); msg.setContent(mp); ByteArrayOutputStream out = new ByteArrayOutputStream(); msg.writeTo(out); InputStream in = new ByteArrayInputStream(out.toByteArray()); MimeMessage newMessage = new MimeMessage(session, in); assertEquals(((InternetAddress)newMessage.getSender()).getAddress(), "foo"); String[] headers = newMessage.getHeader("foo"); assertTrue(headers.length == 1); assertEquals(headers[0], "bar"); newMessage = new MimeMessage(msg); assertEquals(((InternetAddress)newMessage.getSender()).getAddress(), "foo"); assertEquals(newMessage.getHeader("foo")[0], "bar"); } public void testFrom() throws MessagingException { MimeMessage msg = new MimeMessage(session); InternetAddress dev = new InternetAddress("geronimo-dev@apache.org"); InternetAddress user = new InternetAddress("geronimo-user@apache.org"); msg.setSender(dev); Address[] from = msg.getFrom(); assertTrue(from.length == 1); assertEquals(from[0], dev); msg.setFrom(user); from = msg.getFrom(); assertTrue(from.length == 1); assertEquals(from[0], user); msg.addFrom(new Address[] { dev }); from = msg.getFrom(); assertTrue(from.length == 2); assertEquals(from[0], user); assertEquals(from[1], dev); msg.setFrom(); InternetAddress local = InternetAddress.getLocalAddress(session); from = msg.getFrom(); assertTrue(from.length == 1); assertEquals(local, from[0]); msg.setFrom(null); from = msg.getFrom(); assertTrue(from.length == 1); assertEquals(dev, from[0]); msg.setSender(null); from = msg.getFrom(); assertNull(from); } public void testSender() throws MessagingException { MimeMessage msg = new MimeMessage(session); InternetAddress dev = new InternetAddress("geronimo-dev@apache.org"); InternetAddress user = new InternetAddress("geronimo-user@apache.org"); msg.setSender(dev); Address[] from = msg.getFrom(); assertTrue(from.length == 1); assertEquals(from[0], dev); assertEquals(msg.getSender(), dev); msg.setSender(null); assertNull(msg.getSender()); } public void testGetAllRecipients() throws MessagingException { MimeMessage msg = new MimeMessage(session); InternetAddress dev = new InternetAddress("geronimo-dev@apache.org"); InternetAddress user = new InternetAddress("geronimo-user@apache.org"); InternetAddress user1 = new InternetAddress("geronimo-user1@apache.org"); InternetAddress user2 = new InternetAddress("geronimo-user2@apache.org"); NewsAddress group = new NewsAddress("comp.lang.rexx"); Address[] recipients = msg.getAllRecipients(); assertNull(recipients); msg.setRecipients(Message.RecipientType.TO, new Address[] { dev }); recipients = msg.getAllRecipients(); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.addRecipients(Message.RecipientType.BCC, new Address[] { user }); recipients = msg.getAllRecipients(); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.addRecipients(Message.RecipientType.CC, new Address[] { user1, user2} ); recipients = msg.getAllRecipients(); assertTrue(recipients.length == 4); assertEquals(recipients[0], dev); assertEquals(recipients[1], user1); assertEquals(recipients[2], user2); assertEquals(recipients[3], user); msg.addRecipients(MimeMessage.RecipientType.NEWSGROUPS, new Address[] { group } ); recipients = msg.getAllRecipients(); assertTrue(recipients.length == 5); assertEquals(recipients[0], dev); assertEquals(recipients[1], user1); assertEquals(recipients[2], user2); assertEquals(recipients[3], user); assertEquals(recipients[4], group); msg.setRecipients(Message.RecipientType.CC, (String)null); recipients = msg.getAllRecipients(); assertTrue(recipients.length == 3); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); assertEquals(recipients[2], group); } public void testGetRecipients() throws MessagingException { doRecipientTest(Message.RecipientType.TO); doRecipientTest(Message.RecipientType.CC); doRecipientTest(Message.RecipientType.BCC); doNewsgroupRecipientTest(MimeMessage.RecipientType.NEWSGROUPS); } private void doRecipientTest(Message.RecipientType type) throws MessagingException { MimeMessage msg = new MimeMessage(session); InternetAddress dev = new InternetAddress("geronimo-dev@apache.org"); InternetAddress user = new InternetAddress("geronimo-user@apache.org"); Address[] recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, "geronimo-dev@apache.org"); recipients = msg.getRecipients(type); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.addRecipients(type, "geronimo-user@apache.org"); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.setRecipients(type, (String)null); recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, new Address[] { dev }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.addRecipients(type, new Address[] { user }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.setRecipients(type, (Address[])null); recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, new Address[] { dev, user }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); } private void doNewsgroupRecipientTest(Message.RecipientType type) throws MessagingException { MimeMessage msg = new MimeMessage(session); Address dev = new NewsAddress("geronimo-dev"); Address user = new NewsAddress("geronimo-user"); Address[] recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, "geronimo-dev"); recipients = msg.getRecipients(type); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.addRecipients(type, "geronimo-user"); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.setRecipients(type, (String)null); recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, new Address[] { dev }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.addRecipients(type, new Address[] { user }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.setRecipients(type, (Address[])null); recipients = msg.getRecipients(type); assertNull(recipients); msg.setRecipients(type, new Address[] { dev, user }); recipients = msg.getRecipients(type); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); } public void testReplyTo() throws MessagingException { MimeMessage msg = new MimeMessage(session); InternetAddress dev = new InternetAddress("geronimo-dev@apache.org"); InternetAddress user = new InternetAddress("geronimo-user@apache.org"); msg.setReplyTo(new Address[] { dev }); Address[] recipients = msg.getReplyTo(); assertTrue(recipients.length == 1); assertEquals(recipients[0], dev); msg.setReplyTo(new Address[] { dev, user }); recipients = msg.getReplyTo(); assertTrue(recipients.length == 2); assertEquals(recipients[0], dev); assertEquals(recipients[1], user); msg.setReplyTo(null); recipients = msg.getReplyTo(); assertNull(recipients); } public void testSetSubject() throws MessagingException { MimeMessage msg = new MimeMessage(session); String simpleSubject = "Yada, yada"; String complexSubject = "Yada, yada\u0081"; String mungedSubject = "Yada, yada\u003F"; msg.setSubject(simpleSubject); assertEquals(msg.getSubject(), simpleSubject); msg.setSubject(complexSubject, "UTF-8"); assertEquals(msg.getSubject(), complexSubject); msg.setSubject(null); assertNull(msg.getSubject()); } public void testSetDescription() throws MessagingException { MimeMessage msg = new MimeMessage(session); String simpleSubject = "Yada, yada"; String complexSubject = "Yada, yada\u0081"; String mungedSubject = "Yada, yada\u003F"; msg.setDescription(simpleSubject); assertEquals(msg.getDescription(), simpleSubject); msg.setDescription(complexSubject, "UTF-8"); assertEquals(msg.getDescription(), complexSubject); msg.setDescription(null); assertNull(msg.getDescription()); } public void testGetContentType() throws MessagingException { MimeMessage msg = new MimeMessage(session); assertEquals(msg.getContentType(), "text/plain"); msg.setHeader("Content-Type", "text/xml"); assertEquals(msg.getContentType(), "text/xml"); } public void testSetText() throws MessagingException { MimeMessage msg = new MimeMessage(session); msg.setText("Yada, yada"); msg.saveChanges(); ContentType type = new ContentType(msg.getContentType()); assertTrue(type.match("text/plain")); msg = new MimeMessage(session); msg.setText("Yada, yada", "UTF-8"); msg.saveChanges(); type = new ContentType(msg.getContentType()); assertTrue(type.match("text/plain")); assertEquals(type.getParameter("charset"), "UTF-8"); msg = new MimeMessage(session); msg.setText("Yada, yada", "UTF-8", "xml"); msg.saveChanges(); type = new ContentType(msg.getContentType()); assertTrue(type.match("text/xml")); assertEquals(type.getParameter("charset"), "UTF-8"); } protected void setUp() throws Exception { defaultMap = CommandMap.getDefaultCommandMap(); MailcapCommandMap myMap = new MailcapCommandMap(); myMap.addMailcap("text/plain;; x-java-content-handler=" + MimeMultipartTest.DummyTextHandler.class.getName()); myMap.addMailcap("multipart/*;; x-java-content-handler=" + MimeMultipartTest.DummyMultipartHandler.class.getName()); CommandMap.setDefaultCommandMap(myMap); Properties props = new Properties(); props.put("mail.user", "tester"); props.put("mail.host", "apache.org"); session = Session.getInstance(props); } protected void tearDown() throws Exception { CommandMap.setDefaultCommandMap(defaultMap); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/InternetAddressTest.java0000664000175000017500000010745411026734121030506 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.TestCase; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Properties; import javax.mail.Session; /** * @version $Rev: 669901 $ $Date: 2008-06-20 10:01:53 -0400 (Fri, 20 Jun 2008) $ */ public class InternetAddressTest extends TestCase { private InternetAddress address; public void testQuotedLiterals() throws Exception { parseHeaderTest("\"Foo\t\n\\\\\\\"\" ", true, "foo@apache.org", "Foo\t\n\\\"", "\"Foo\t\n\\\\\\\"\" ", false); parseHeaderTest("<\"@,:;<>.[]()\"@apache.org>", true, "\"@,:;<>.[]()\"@apache.org", null, "<\"@,:;<>.[]()\"@apache.org>", false); parseHeaderTest("<\"\\F\\o\\o\"@apache.org>", true, "\"Foo\"@apache.org", null, "<\"Foo\"@apache.org>", false); parseHeaderErrorTest("\"Foo ", true); parseHeaderErrorTest("\"Foo\r\" ", true); } public void testDomainLiterals() throws Exception { parseHeaderTest("", true, "foo@[apache].org", null, "", false); parseHeaderTest(".,:;\"\\\\].org>", true, "foo@[@()<>.,:;\"\\\\].org", null, ".,:;\"\\\\].org>", false); parseHeaderTest("", true, "foo@[\\[\\]].org", null, "", false); parseHeaderErrorTest("", true); parseHeaderErrorTest("", true); parseHeaderErrorTest("", true); } public void testComments() throws Exception { parseHeaderTest("Foo Bar (Fred) ", true, "foo@apache.org", "Foo Bar (Fred)", "\"Foo Bar (Fred)\" ", false); parseHeaderTest("(Fred) foo@apache.org", true, "foo@apache.org", "Fred", "Fred ", false); parseHeaderTest("(\\(Fred\\)) foo@apache.org", true, "foo@apache.org", "(Fred)", "\"(Fred)\" ", false); parseHeaderTest("(Fred (Jones)) foo@apache.org", true, "foo@apache.org", "Fred (Jones)", "\"Fred (Jones)\" ", false); parseHeaderErrorTest("(Fred foo@apache.org", true); parseHeaderErrorTest("(Fred\r) foo@apache.org", true); } public void testParseHeader() throws Exception { parseHeaderTest("<@apache.org,@apache.net:foo@apache.org>", false, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); parseHeaderTest("<@apache.org:foo@apache.org>", false, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); parseHeaderTest("Foo Bar:;", false, "Foo Bar:;", null, "Foo Bar:;", true); parseHeaderTest("\"\\\"Foo Bar\" ", false, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); parseHeaderTest("\"Foo Bar\" ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("(Foo) (Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo", "Foo ", false); parseHeaderTest("", false, "foo@apache.org", null, "foo@apache.org", false); parseHeaderTest("Foo Bar ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("foo", false, "foo", null, "foo", false); parseHeaderTest("\"foo\"", false, "\"foo\"", null, "<\"foo\">", false); parseHeaderTest("foo@apache.org", false, "foo@apache.org", null, "foo@apache.org", false); parseHeaderTest("\"foo\"@apache.org", false, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); parseHeaderTest("foo@[apache].org", false, "foo@[apache].org", null, "", false); parseHeaderTest("foo@[apache].[org]", false, "foo@[apache].[org]", null, "", false); parseHeaderTest("foo.bar@apache.org", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("(Foo Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("(Foo) (Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("\"Foo\" Bar ", false, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); parseHeaderTest("(Foo Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("apache.org", false, "apache.org", null, "apache.org", false); } public void testValidate() throws Exception { validateTest("@apache.org,@apache.net:foo@apache.org"); validateTest("@apache.org:foo@apache.org"); validateTest("Foo Bar:;"); validateTest("foo.bar@apache.org"); validateTest("bar@apache.org"); validateTest("foo"); validateTest("foo.bar"); validateTest("\"foo\""); validateTest("\"foo\"@apache.org"); validateTest("foo@[apache].org"); validateTest("foo@[apache].[org]"); } public void testStrictParseHeader() throws Exception { parseHeaderTest("<@apache.org,@apache.net:foo@apache.org>", true, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); parseHeaderTest("<@apache.org:foo@apache.org>", true, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); parseHeaderTest("Foo Bar:;", true, "Foo Bar:;", null, "Foo Bar:;", true); parseHeaderTest("\"\\\"Foo Bar\" ", true, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); parseHeaderTest("\"Foo Bar\" ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("(Foo) (Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo", "Foo ", false); parseHeaderTest("", true, "foo@apache.org", null, "foo@apache.org", false); parseHeaderTest("Foo Bar ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("foo", true, "foo", null, "foo", false); parseHeaderTest("\"foo\"", true, "\"foo\"", null, "<\"foo\">", false); parseHeaderTest("foo@apache.org", true, "foo@apache.org", null, "foo@apache.org", false); parseHeaderTest("\"foo\"@apache.org", true, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); parseHeaderTest("foo@[apache].org", true, "foo@[apache].org", null, "", false); parseHeaderTest("foo@[apache].[org]", true, "foo@[apache].[org]", null, "", false); parseHeaderTest("foo.bar@apache.org", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("(Foo Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("(Foo) (Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseHeaderTest("\"Foo\" Bar ", true, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); parseHeaderTest("(Foo Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseHeaderTest("apache.org", true, "apache.org", null, "apache.org", false); } public void testParse() throws Exception { parseTest("<@apache.org,@apache.net:foo@apache.org>", false, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); parseTest("<@apache.org:foo@apache.org>", false, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); parseTest("Foo Bar:;", false, "Foo Bar:;", null, "Foo Bar:;", true); parseTest("\"\\\"Foo Bar\" ", false, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); parseTest("\"Foo Bar\" ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("(Foo) (Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo", "Foo ", false); parseTest("", false, "foo@apache.org", null, "foo@apache.org", false); parseTest("Foo Bar ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("foo", false, "foo", null, "foo", false); parseTest("\"foo\"", false, "\"foo\"", null, "<\"foo\">", false); parseTest("foo@apache.org", false, "foo@apache.org", null, "foo@apache.org", false); parseTest("\"foo\"@apache.org", false, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); parseTest("foo@[apache].org", false, "foo@[apache].org", null, "", false); parseTest("foo@[apache].[org]", false, "foo@[apache].[org]", null, "", false); parseTest("foo.bar@apache.org", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("(Foo Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("(Foo) (Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("\"Foo\" Bar ", false, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); parseTest("(Foo Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("apache.org", false, "apache.org", null, "apache.org", false); } public void testDefaultParse() throws Exception { parseDefaultTest("<@apache.org,@apache.net:foo@apache.org>", "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); parseDefaultTest("<@apache.org:foo@apache.org>", "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); parseDefaultTest("Foo Bar:;", "Foo Bar:;", null, "Foo Bar:;", true); parseDefaultTest("\"\\\"Foo Bar\" ", "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); parseDefaultTest("\"Foo Bar\" ", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseDefaultTest("(Foo) (Bar) foo.bar@apache.org", "foo.bar@apache.org", "Foo", "Foo ", false); parseDefaultTest("", "foo@apache.org", null, "foo@apache.org", false); parseDefaultTest("Foo Bar ", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseDefaultTest("foo", "foo", null, "foo", false); parseDefaultTest("\"foo\"", "\"foo\"", null, "<\"foo\">", false); parseDefaultTest("foo@apache.org", "foo@apache.org", null, "foo@apache.org", false); parseDefaultTest("\"foo\"@apache.org", "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); parseDefaultTest("foo@[apache].org", "foo@[apache].org", null, "", false); parseDefaultTest("foo@[apache].[org]", "foo@[apache].[org]", null, "", false); parseDefaultTest("foo.bar@apache.org", "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseDefaultTest("(Foo Bar) ", "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseDefaultTest("(Foo) (Bar) ", "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseDefaultTest("\"Foo\" Bar ", "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); parseDefaultTest("(Foo Bar) foo.bar@apache.org", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseDefaultTest("apache.org", "apache.org", null, "apache.org", false); } public void testStrictParse() throws Exception { parseTest("<@apache.org,@apache.net:foo@apache.org>", true, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); parseTest("<@apache.org:foo@apache.org>", true, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); parseTest("Foo Bar:;", true, "Foo Bar:;", null, "Foo Bar:;", true); parseTest("\"\\\"Foo Bar\" ", true, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); parseTest("\"Foo Bar\" ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("(Foo) (Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo", "Foo ", false); parseTest("", true, "foo@apache.org", null, "foo@apache.org", false); parseTest("Foo Bar ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("foo", true, "foo", null, "foo", false); parseTest("\"foo\"", true, "\"foo\"", null, "<\"foo\">", false); parseTest("foo@apache.org", true, "foo@apache.org", null, "foo@apache.org", false); parseTest("\"foo\"@apache.org", true, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); parseTest("foo@[apache].org", true, "foo@[apache].org", null, "", false); parseTest("foo@[apache].[org]", true, "foo@[apache].[org]", null, "", false); parseTest("foo.bar@apache.org", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("(Foo Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("(Foo) (Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); parseTest("\"Foo\" Bar ", true, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); parseTest("(Foo Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); parseTest("apache.org", true, "apache.org", null, "apache.org", false); } public void testConstructor() throws Exception { constructorTest("(Foo) (Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo", "Foo ", false); constructorTest("<@apache.org,@apache.net:foo@apache.org>", false, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); constructorTest("<@apache.org:foo@apache.org>", false, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); constructorTest("Foo Bar:;", false, "Foo Bar:;", null, "Foo Bar:;", true); constructorTest("\"\\\"Foo Bar\" ", false, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); constructorTest("\"Foo Bar\" ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("", false, "foo@apache.org", null, "foo@apache.org", false); constructorTest("Foo Bar ", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("foo", false, "foo", null, "foo", false); constructorTest("\"foo\"", false, "\"foo\"", null, "<\"foo\">", false); constructorTest("foo@apache.org", false, "foo@apache.org", null, "foo@apache.org", false); constructorTest("\"foo\"@apache.org", false, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); constructorTest("foo@[apache].org", false, "foo@[apache].org", null, "", false); constructorTest("foo@[apache].[org]", false, "foo@[apache].[org]", null, "", false); constructorTest("foo.bar@apache.org", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("(Foo Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("(Foo) (Bar) ", false, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("\"Foo\" Bar ", false, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); constructorTest("(Foo Bar) foo.bar@apache.org", false, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("apache.org", false, "apache.org", null, "apache.org", false); } public void testDefaultConstructor() throws Exception { constructorDefaultTest("<@apache.org,@apache.net:foo@apache.org>", "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); constructorDefaultTest("<@apache.org:foo@apache.org>", "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); constructorDefaultTest("Foo Bar:;", "Foo Bar:;", null, "Foo Bar:;", true); constructorDefaultTest("\"\\\"Foo Bar\" ", "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); constructorDefaultTest("\"Foo Bar\" ", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorDefaultTest("(Foo) (Bar) foo.bar@apache.org", "foo.bar@apache.org", "Foo", "Foo ", false); constructorDefaultTest("", "foo@apache.org", null, "foo@apache.org", false); constructorDefaultTest("Foo Bar ", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorDefaultTest("foo", "foo", null, "foo", false); constructorDefaultTest("\"foo\"", "\"foo\"", null, "<\"foo\">", false); constructorDefaultTest("foo@apache.org", "foo@apache.org", null, "foo@apache.org", false); constructorDefaultTest("\"foo\"@apache.org", "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); constructorDefaultTest("foo@[apache].org", "foo@[apache].org", null, "", false); constructorDefaultTest("foo@[apache].[org]", "foo@[apache].[org]", null, "", false); constructorDefaultTest("foo.bar@apache.org", "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorDefaultTest("(Foo Bar) ", "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorDefaultTest("(Foo) (Bar) ", "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorDefaultTest("\"Foo\" Bar ", "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); constructorDefaultTest("(Foo Bar) foo.bar@apache.org", "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorDefaultTest("apache.org", "apache.org", null, "apache.org", false); } public void testStrictConstructor() throws Exception { constructorTest("<@apache.org,@apache.net:foo@apache.org>", true, "@apache.org,@apache.net:foo@apache.org", null, "<@apache.org,@apache.net:foo@apache.org>", false); constructorTest("<@apache.org:foo@apache.org>", true, "@apache.org:foo@apache.org", null, "<@apache.org:foo@apache.org>", false); constructorTest("Foo Bar:;", true, "Foo Bar:;", null, "Foo Bar:;", true); constructorTest("\"\\\"Foo Bar\" ", true, "foo.bar@apache.org", "\"Foo Bar", "\"\\\"Foo Bar\" ", false); constructorTest("\"Foo Bar\" ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("(Foo) (Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo", "Foo ", false); constructorTest("", true, "foo@apache.org", null, "foo@apache.org", false); constructorTest("Foo Bar ", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("foo", true, "foo", null, "foo", false); constructorTest("\"foo\"", true, "\"foo\"", null, "<\"foo\">", false); constructorTest("foo@apache.org", true, "foo@apache.org", null, "foo@apache.org", false); constructorTest("\"foo\"@apache.org", true, "\"foo\"@apache.org", null, "<\"foo\"@apache.org>", false); constructorTest("foo@[apache].org", true, "foo@[apache].org", null, "", false); constructorTest("foo@[apache].[org]", true, "foo@[apache].[org]", null, "", false); constructorTest("foo.bar@apache.org", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("(Foo Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("(Foo) (Bar) ", true, "foo.bar@apache.org", null, "foo.bar@apache.org", false); constructorTest("\"Foo\" Bar ", true, "foo.bar@apache.org", "\"Foo\" Bar", "\"\\\"Foo\\\" Bar\" ", false); constructorTest("(Foo Bar) foo.bar@apache.org", true, "foo.bar@apache.org", "Foo Bar", "Foo Bar ", false); constructorTest("apache.org", true, "apache.org", null, "apache.org", false); } public void testParseHeaderList() throws Exception { InternetAddress[] addresses = InternetAddress.parseHeader("foo@apache.org,bar@apache.org", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = InternetAddress.parseHeader("Foo ,,Bar ", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", "Foo", "Foo ", false); validateAddress(addresses[1], "bar@apache.org", "Bar", "Bar ", false); addresses = InternetAddress.parseHeader("foo@apache.org, bar@apache.org", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = InternetAddress.parseHeader("Foo , Bar ", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", "Foo", "Foo ", false); validateAddress(addresses[1], "bar@apache.org", "Bar", "Bar ", false); addresses = InternetAddress.parseHeader("Foo ,(yada),Bar ", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", "Foo", "Foo ", false); validateAddress(addresses[1], "bar@apache.org", "Bar", "Bar ", false); } public void testParseHeaderErrors() throws Exception { parseHeaderErrorTest("foo@apache.org bar@apache.org", true); parseHeaderErrorTest("Foo foo@apache.org", true); parseHeaderErrorTest("Foo foo@apache.org", true); parseHeaderErrorTest("Foo ,bar@apache.org;", true, "Foo Bar:,bar@apache.org;", null, "Foo Bar:,bar@apache.org;", true); parseHeaderTest("Foo Bar:Foo ,bar@apache.org;", true, "Foo Bar:Foo,bar@apache.org;", null, "Foo Bar:Foo,bar@apache.org;", true); parseHeaderTest("Foo:,,bar@apache.org;", true, "Foo:,,bar@apache.org;", null, "Foo:,,bar@apache.org;", true); parseHeaderTest("Foo:foo,bar;", true, "Foo:foo,bar;", null, "Foo:foo,bar;", true); parseHeaderTest("Foo:;", true, "Foo:;", null, "Foo:;", true); parseHeaderTest("\"Foo\":foo@apache.org;", true, "\"Foo\":foo@apache.org;", null, "\"Foo\":foo@apache.org;", true); parseHeaderErrorTest("Foo:foo@apache.org,bar@apache.org", true); parseHeaderErrorTest("Foo:foo@apache.org,Bar:bar@apache.org;;", true); parseHeaderErrorTest(":foo@apache.org;", true); parseHeaderErrorTest("Foo Bar:,bar@apache.org;", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:,,bar@apache.org;", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:Foo ,bar@apache.org;", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", "Foo", "Foo ", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:Foo <@apache.org:foo@apache.org>,bar@apache.org;", true); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "@apache.org:foo@apache.org", "Foo", "Foo <@apache.org:foo@apache.org>", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:;", true); assertTrue("Expecting 0 addresses", addresses.length == 0); addresses = getGroup("Foo:foo@apache.org;", false); assertTrue("Expecting 1 address", addresses.length == 1); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); addresses = getGroup("Foo:foo@apache.org,bar@apache.org;", false); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:,bar@apache.org;", false); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:,,bar@apache.org;", false); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", null, "foo@apache.org", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:Foo ,bar@apache.org;", false); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "foo@apache.org", "Foo", "Foo ", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:Foo <@apache.org:foo@apache.org>,bar@apache.org;", false); assertTrue("Expecting 2 addresses", addresses.length == 2); validateAddress(addresses[0], "@apache.org:foo@apache.org", "Foo", "Foo <@apache.org:foo@apache.org>", false); validateAddress(addresses[1], "bar@apache.org", null, "bar@apache.org", false); addresses = getGroup("Foo:;", false); assertTrue("Expecting 0 addresses", addresses.length == 0); } public void testLocalAddress() throws Exception { System.getProperties().remove("user.name"); assertNull(InternetAddress.getLocalAddress(null)); System.setProperty("user.name", "dev"); InternetAddress localHost = null; String user = null; String host = null; try { user = System.getProperty("user.name"); host = InetAddress.getLocalHost().getHostName(); localHost = new InternetAddress(user + "@" + host); } catch (AddressException e) { // ignore } catch (UnknownHostException e) { // ignore } catch (SecurityException e) { // ignore } assertEquals(InternetAddress.getLocalAddress(null), localHost); Properties props = new Properties(); Session session = Session.getInstance(props, null); assertEquals(InternetAddress.getLocalAddress(session), localHost); props.put("mail.host", "apache.org"); session = Session.getInstance(props, null); assertEquals(InternetAddress.getLocalAddress(session), new InternetAddress(user + "@apache.org")); props.put("mail.user", "user"); props.remove("mail.host"); session = Session.getInstance(props, null); assertEquals(InternetAddress.getLocalAddress(session), new InternetAddress("user@" + host)); props.put("mail.host", "apache.org"); session = Session.getInstance(props, null); assertEquals(InternetAddress.getLocalAddress(session), new InternetAddress("user@apache.org")); props.put("mail.from", "tester@incubator.apache.org"); session = Session.getInstance(props, null); assertEquals(InternetAddress.getLocalAddress(session), new InternetAddress("tester@incubator.apache.org")); } private InternetAddress[] getGroup(String address, boolean strict) throws AddressException { InternetAddress group = new InternetAddress(address); return group.getGroup(strict); } protected void setUp() throws Exception { address = new InternetAddress(); } private void parseHeaderTest(String address, boolean strict, String resultAddr, String personal, String toString, boolean group) throws Exception { InternetAddress[] addresses = InternetAddress.parseHeader(address, strict); assertTrue(addresses.length == 1); validateAddress(addresses[0], resultAddr, personal, toString, group); } private void parseHeaderErrorTest(String address, boolean strict) throws Exception { try { InternetAddress.parseHeader(address, strict); fail("Expected AddressException"); } catch (AddressException e) { } } private void constructorTest(String address, boolean strict, String resultAddr, String personal, String toString, boolean group) throws Exception { validateAddress(new InternetAddress(address, strict), resultAddr, personal, toString, group); } private void constructorDefaultTest(String address, String resultAddr, String personal, String toString, boolean group) throws Exception { validateAddress(new InternetAddress(address), resultAddr, personal, toString, group); } private void constructorErrorTest(String address, boolean strict) throws Exception { try { InternetAddress foo = new InternetAddress(address, strict); fail("Expected AddressException"); } catch (AddressException e) { } } private void parseTest(String address, boolean strict, String resultAddr, String personal, String toString, boolean group) throws Exception { InternetAddress[] addresses = InternetAddress.parse(address, strict); assertTrue(addresses.length == 1); validateAddress(addresses[0], resultAddr, personal, toString, group); } private void parseErrorTest(String address, boolean strict) throws Exception { try { InternetAddress.parse(address, strict); fail("Expected AddressException"); } catch (AddressException e) { } } private void parseDefaultTest(String address, String resultAddr, String personal, String toString, boolean group) throws Exception { InternetAddress[] addresses = InternetAddress.parse(address); assertTrue(addresses.length == 1); validateAddress(addresses[0], resultAddr, personal, toString, group); } private void parseDefaultErrorTest(String address) throws Exception { try { InternetAddress.parse(address); fail("Expected AddressException"); } catch (AddressException e) { } } private void validateTest(String address) throws Exception { InternetAddress test = new InternetAddress(); test.setAddress(address); test.validate(); } private void validateErrorTest(String address) throws Exception { InternetAddress test = new InternetAddress(); test.setAddress(address); try { test.validate(); fail("Expected AddressException"); } catch (AddressException e) { } } private void validateAddress(InternetAddress a, String address, String personal, String toString, boolean group) { assertEquals("Invalid address:", a.getAddress(), address); if (personal == null) { assertNull("Personal must be null", a.getPersonal()); } else { assertEquals("Invalid Personal:", a.getPersonal(), personal); } assertEquals("Invalid string value:", a.toString(), toString); assertTrue("Incorrect group value:", group == a.isGroup()); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/ContentDispositionTest.java0000664000175000017500000000451310517560657031256 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class ContentDispositionTest extends TestCase { public ContentDispositionTest(String name) { super(name); } public void testContentDisposition() throws ParseException { ContentDisposition c; c = new ContentDisposition(); assertNotNull(c.getParameterList()); assertNull(c.getParameterList().get("nothing")); assertNull(c.getDisposition()); assertNull(c.toString()); c.setDisposition("inline"); assertEquals("inline",c.getDisposition()); c.setParameter("file","file.txt"); assertEquals("file.txt",c.getParameterList().get("file")); assertEquals("inline; file=file.txt",c.toString()); c = new ContentDisposition("inline"); assertEquals(0,c.getParameterList().size()); assertEquals("inline",c.getDisposition()); c = new ContentDisposition("inline",new ParameterList(";charset=us-ascii;content-type=\"text/plain\"")); assertEquals("inline",c.getDisposition()); assertEquals("us-ascii",c.getParameter("charset")); assertEquals("text/plain",c.getParameter("content-type")); c = new ContentDisposition("attachment;content-type=\"text/html\";charset=UTF-8"); assertEquals("attachment",c.getDisposition()); assertEquals("UTF-8",c.getParameter("charset")); assertEquals("text/html",c.getParameter("content-type")); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/NewsAddressTest.java0000664000175000017500000000336410517560657027644 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class NewsAddressTest extends TestCase { public void testNewsAddress() throws AddressException { NewsAddress na = new NewsAddress("geronimo-dev", "news.apache.org"); assertEquals("geronimo-dev", na.getNewsgroup()); assertEquals("news.apache.org", na.getHost()); assertEquals("news", na.getType()); assertEquals("geronimo-dev", na.toString()); NewsAddress[] nas = NewsAddress.parse( "geronimo-dev@news.apache.org, geronimo-user@news.apache.org"); assertEquals(2, nas.length); assertEquals("geronimo-dev", nas[0].getNewsgroup()); assertEquals("news.apache.org", nas[0].getHost()); assertEquals("geronimo-user", nas[1].getNewsgroup()); assertEquals("news.apache.org", nas[1].getHost()); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/HeaderTokenizerTest.java0000664000175000017500000001362110517560657030502 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import javax.mail.internet.HeaderTokenizer.Token; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class HeaderTokenizerTest extends TestCase { public void testTokenizer() throws ParseException { Token t; HeaderTokenizer ht; ht = new HeaderTokenizer("To: \"Geronimo List\" , \n\r Geronimo User "); validateToken(ht.peek(), Token.ATOM, "To"); validateToken(ht.next(), Token.ATOM, "To"); validateToken(ht.peek(), ':', ":"); validateToken(ht.next(), ':', ":"); validateToken(ht.next(), Token.QUOTEDSTRING, "Geronimo List"); validateToken(ht.next(), '<', "<"); validateToken(ht.next(), Token.ATOM, "geronimo-dev"); validateToken(ht.next(), '@', "@"); validateToken(ht.next(), Token.ATOM, "apache"); validateToken(ht.next(), '.', "."); validateToken(ht.next(), Token.ATOM, "org"); validateToken(ht.next(), '>', ">"); validateToken(ht.next(), ',', ","); validateToken(ht.next(), Token.ATOM, "Geronimo"); validateToken(ht.next(), Token.ATOM, "User"); validateToken(ht.next(), '<', "<"); validateToken(ht.next(), Token.ATOM, "geronimo-user"); validateToken(ht.next(), '@', "@"); validateToken(ht.next(), Token.ATOM, "apache"); validateToken(ht.next(), '.', "."); assertEquals("org>", ht.getRemainder()); validateToken(ht.peek(), Token.ATOM, "org"); validateToken(ht.next(), Token.ATOM, "org"); validateToken(ht.next(), '>', ">"); assertEquals(Token.EOF, ht.next().getType()); ht = new HeaderTokenizer(" "); assertEquals(Token.EOF, ht.next().getType()); ht = new HeaderTokenizer("J2EE"); validateToken(ht.next(), Token.ATOM, "J2EE"); assertEquals(Token.EOF, ht.next().getType()); // test comments doComment(true); doComment(false); } public void testErrors() throws ParseException { checkParseError("(Geronimo"); checkParseError("((Geronimo)"); checkParseError("\"Geronimo"); checkParseError("\"Geronimo\\"); } public void testQuotedLiteral() throws ParseException { checkTokenParse("\"\"", Token.QUOTEDSTRING, ""); checkTokenParse("\"\\\"\"", Token.QUOTEDSTRING, "\""); checkTokenParse("\"\\\"\"", Token.QUOTEDSTRING, "\""); checkTokenParse("\"A\r\nB\"", Token.QUOTEDSTRING, "AB"); checkTokenParse("\"A\nB\"", Token.QUOTEDSTRING, "A\nB"); } public void testComment() throws ParseException { checkTokenParse("()", Token.COMMENT, ""); checkTokenParse("(())", Token.COMMENT, "()"); checkTokenParse("(Foo () Bar)", Token.COMMENT, "Foo () Bar"); checkTokenParse("(\"Foo () Bar)", Token.COMMENT, "\"Foo () Bar"); checkTokenParse("(\\()", Token.COMMENT, "("); checkTokenParse("(Foo \r\n Bar)", Token.COMMENT, "Foo Bar"); checkTokenParse("(Foo \n Bar)", Token.COMMENT, "Foo \n Bar"); } public void checkTokenParse(String text, int type, String value) throws ParseException { HeaderTokenizer ht; ht = new HeaderTokenizer(text, HeaderTokenizer.RFC822, false); validateToken(ht.next(), type, value); } public void checkParseError(String text) throws ParseException { Token t; HeaderTokenizer ht; ht = new HeaderTokenizer(text); doNextError(ht); ht = new HeaderTokenizer(text); doPeekError(ht); } public void doNextError(HeaderTokenizer ht) { try { ht.next(); fail("Expected ParseException"); } catch (ParseException e) { } } public void doPeekError(HeaderTokenizer ht) { try { ht.peek(); fail("Expected ParseException"); } catch (ParseException e) { } } public void doComment(boolean ignore) throws ParseException { HeaderTokenizer ht; Token t; ht = new HeaderTokenizer( "Apache(Geronimo)J2EE", HeaderTokenizer.RFC822, ignore); validateToken(ht.next(), Token.ATOM, "Apache"); if (!ignore) { validateToken(ht.next(), Token.COMMENT, "Geronimo"); } validateToken(ht.next(), Token.ATOM, "J2EE"); assertEquals(Token.EOF, ht.next().getType()); ht = new HeaderTokenizer( "Apache(Geronimo (Project))J2EE", HeaderTokenizer.RFC822, ignore); validateToken(ht.next(), Token.ATOM, "Apache"); if (!ignore) { validateToken(ht.next(), Token.COMMENT, "Geronimo (Project)"); } validateToken(ht.next(), Token.ATOM, "J2EE"); assertEquals(Token.EOF, ht.next().getType()); } private void validateToken(HeaderTokenizer.Token token, int type, String value) { assertEquals(token.getType(), type); assertEquals(token.getValue(), value); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/ParameterListTest.java0000664000175000017500000000445610517560657030201 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class ParameterListTest extends TestCase { public ParameterListTest(String arg0) { super(arg0); } public void testParameters() throws ParseException { ParameterList list = new ParameterList(";thing=value;thong=vulue;thung=git"); assertEquals("value", list.get("thing")); assertEquals("vulue", list.get("thong")); assertEquals("git", list.get("thung")); } public void testQuotedParameter() throws ParseException { ParameterList list = new ParameterList(";foo=one;bar=\"two\""); assertEquals("one", list.get("foo")); assertEquals("two", list.get("bar")); } public void testEncodeDecode() throws Exception { System.setProperty("mail.mime.encodeparameters", "true"); System.setProperty("mail.mime.decodeparameters", "true"); String value = " '*% abc \u0081\u0082\r\n\t"; String encodedTest = "; one*=UTF-8''%20%27%2A%25%20abc%20%C2%81%C2%82%0D%0A%09"; ParameterList list = new ParameterList(); list.set("one", value, "UTF-8"); assertEquals(value, list.get("one")); String encoded = list.toString(); assertEquals(encoded, encodedTest); ParameterList list2 = new ParameterList(encoded); assertEquals(value, list.get("one")); assertEquals(list2.toString(), encodedTest); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/internet/MimeUtilityTest.java0000664000175000017500000002101010754634312027652 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.internet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.Session; import javax.mail.util.ByteArrayDataSource; import junit.framework.TestCase; public class MimeUtilityTest extends TestCase { private byte[] encodeBytes = new byte[] { 32, 104, -61, -87, 33, 32, -61, -96, -61, -88, -61, -76, 117, 32, 33, 33, 33 }; public void testEncodeDecode() throws Exception { byte [] data = new byte[256]; for (int i = 0; i < data.length; i++) { data[i] = (byte)i; } // different lengths test boundary conditions doEncodingTest(data, 256, "uuencode"); doEncodingTest(data, 255, "uuencode"); doEncodingTest(data, 254, "uuencode"); doEncodingTest(data, 256, "binary"); doEncodingTest(data, 256, "7bit"); doEncodingTest(data, 256, "8bit"); doEncodingTest(data, 256, "base64"); doEncodingTest(data, 255, "base64"); doEncodingTest(data, 254, "base64"); doEncodingTest(data, 256, "x-uuencode"); doEncodingTest(data, 256, "x-uue"); doEncodingTest(data, 256, "quoted-printable"); doEncodingTest(data, 255, "quoted-printable"); doEncodingTest(data, 254, "quoted-printable"); } public void testFoldUnfold() throws Exception { doFoldTest(0, "This is a short string", "This is a short string"); doFoldTest(0, "The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog.", "The quick brown fox jumped over the lazy dog. The quick brown fox jumped\r\n over the lazy dog. The quick brown fox jumped over the lazy dog."); doFoldTest(50, "The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog.", "The quick brown fox jumped\r\n over the lazy dog. The quick brown fox jumped over the lazy dog. The quick\r\n brown fox jumped over the lazy dog."); doFoldTest(20, "======================================================================================================================= break should be here", "=======================================================================================================================\r\n break should be here"); } public void doEncodingTest(byte[] data, int length, String encoding) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); OutputStream encoder = MimeUtility.encode(out, encoding); encoder.write(data, 0, length); encoder.flush(); byte[] encodedData = out.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(encodedData); InputStream decoder = MimeUtility.decode(in, encoding); byte[] decodedData = new byte[length]; int count = decoder.read(decodedData); assertEquals(length, count); for (int i = 0; i < length; i++) { assertEquals(data[i], decodedData[i]); } } public void doFoldTest(int used, String source, String folded) throws Exception { String newFolded = MimeUtility.fold(used, source); String newUnfolded = MimeUtility.unfold(newFolded); assertEquals(folded, newFolded); assertEquals(source, newUnfolded); } public void testEncodeWord() throws Exception { assertEquals("abc", MimeUtility.encodeWord("abc")); String encodeString = new String(encodeBytes, "UTF-8"); // default code page dependent, hard to directly test the encoded results // The following disabled because it will not succeed on all locales because the // code points used in the test string won't round trip properly for all code pages. // assertEquals(encodeString, MimeUtility.decodeWord(MimeUtility.encodeWord(encodeString))); String encoded = MimeUtility.encodeWord(encodeString, "UTF-8", "Q"); assertEquals("=?UTF-8?Q?_h=C3=A9!_=C3=A0=C3=A8=C3=B4u_!!!?=", encoded); assertEquals(encodeString, MimeUtility.decodeWord(encoded)); encoded = MimeUtility.encodeWord(encodeString, "UTF-8", "B"); assertEquals("=?UTF-8?B?IGjDqSEgw6DDqMO0dSAhISE=?=", encoded); assertEquals(encodeString, MimeUtility.decodeWord(encoded)); } public void testEncodeText() throws Exception { assertEquals("abc", MimeUtility.encodeWord("abc")); String encodeString = new String(encodeBytes, "UTF-8"); // default code page dependent, hard to directly test the encoded results // The following disabled because it will not succeed on all locales because the // code points used in the test string won't round trip properly for all code pages. // assertEquals(encodeString, MimeUtility.decodeText(MimeUtility.encodeText(encodeString))); String encoded = MimeUtility.encodeText(encodeString, "UTF-8", "Q"); assertEquals("=?UTF-8?Q?_h=C3=A9!_=C3=A0=C3=A8=C3=B4u_!!!?=", encoded); assertEquals(encodeString, MimeUtility.decodeText(encoded)); encoded = MimeUtility.encodeText(encodeString, "UTF-8", "B"); assertEquals("=?UTF-8?B?IGjDqSEgw6DDqMO0dSAhISE=?=", encoded); assertEquals(encodeString, MimeUtility.decodeText(encoded)); // this has multiple byte characters and is longer than the 76 character grouping, so this // hits a lot of different boundary conditions String subject = "\u03a0\u03a1\u03a2\u03a3\u03a4\u03a5\u03a6\u03a7 \u03a8\u03a9\u03aa\u03ab \u03ac\u03ad\u03ae\u03af\u03b0 \u03b1\u03b2\u03b3\u03b4\u03b5 \u03b6\u03b7\u03b8\u03b9\u03ba \u03bb\u03bc\u03bd\u03be\u03bf\u03c0 \u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7 \u03c8\u03c9\u03ca\u03cb\u03cd\u03ce \u03cf\u03d0\u03d1\u03d2"; encoded = MimeUtility.encodeText(subject, "utf-8", "Q"); assertEquals(subject, MimeUtility.decodeText(encoded)); encoded = MimeUtility.encodeText(subject, "utf-8", "B"); assertEquals(subject, MimeUtility.decodeText(encoded)); } public void testGetEncoding() throws Exception { ByteArrayDataSource source = new ByteArrayDataSource(new byte[] { 'a', 'b', 'c'}, "text/plain"); assertEquals("7bit", MimeUtility.getEncoding(source)); source = new ByteArrayDataSource(new byte[] { 'a', 'b', (byte)0x81}, "text/plain"); assertEquals("quoted-printable", MimeUtility.getEncoding(source)); source = new ByteArrayDataSource(new byte[] { 'a', (byte)0x82, (byte)0x81}, "text/plain"); assertEquals("base64", MimeUtility.getEncoding(source)); source = new ByteArrayDataSource(new byte[] { 'a', 'b', 'c'}, "application/binary"); assertEquals("7bit", MimeUtility.getEncoding(source)); source = new ByteArrayDataSource(new byte[] { 'a', 'b', (byte)0x81}, "application/binary"); assertEquals("base64", MimeUtility.getEncoding(source)); source = new ByteArrayDataSource(new byte[] { 'a', (byte)0x82, (byte)0x81}, "application/binary"); assertEquals("base64", MimeUtility.getEncoding(source)); } public void testQuote() throws Exception { assertEquals("abc", MimeUtility.quote("abc", "&*%")); assertEquals("\"abc&\"", MimeUtility.quote("abc&", "&*%")); assertEquals("\"abc\\\"\"", MimeUtility.quote("abc\"", "&*%")); assertEquals("\"abc\\\\\"", MimeUtility.quote("abc\\", "&*%")); assertEquals("\"abc\\\r\"", MimeUtility.quote("abc\r", "&*%")); assertEquals("\"abc\\\n\"", MimeUtility.quote("abc\n", "&*%")); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/AllTests.java0000664000175000017500000000320010517560657024452 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import javax.mail.event.AllEventTests; import javax.mail.internet.AllInternetTests; import junit.framework.Test; import junit.framework.TestSuite; /** * @version $Revision $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class AllTests { public static Test suite() { TestSuite suite = new TestSuite("Test for javax.mail"); //$JUnit-BEGIN$ suite.addTest(new TestSuite(FlagsTest.class)); suite.addTest(new TestSuite(HeaderTest.class)); suite.addTest(new TestSuite(MessagingExceptionTest.class)); suite.addTest(new TestSuite(URLNameTest.class)); suite.addTest(new TestSuite(PasswordAuthenticationTest.class)); suite.addTest(AllEventTests.suite()); suite.addTest(AllInternetTests.suite()); //$JUnit-END$ return suite; } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/HeaderTest.java0000664000175000017500000000233310517560657024755 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class HeaderTest extends TestCase { public HeaderTest(String name) { super(name); } public void testHeader() { Header header = new Header("One", "Two"); assertEquals("One", header.getName()); assertEquals("Two", header.getValue()); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/SessionTest.java0000664000175000017500000000500510517560657025207 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.Properties; import javax.mail.MessagingException; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class SessionTest extends TestCase { public void testAddProvider() throws MessagingException { Properties props = System.getProperties(); // Get a Session object Session mailSession = Session.getDefaultInstance(props, null); mailSession.addProvider(new Provider(Provider.Type.TRANSPORT, "foo", NullTransport.class.getName(), "Apache", "Java 1.4 Test")); // retrieve the transport Transport trans = mailSession.getTransport("foo"); assertTrue(trans instanceof NullTransport); mailSession.setProtocolForAddress("foo", "foo"); trans = mailSession.getTransport(new FooAddress()); assertTrue(trans instanceof NullTransport); } static public class NullTransport extends Transport { public NullTransport(Session session, URLName urlName) { super(session, urlName); } public void sendMessage(Message message, Address[] addresses) throws MessagingException { // do nothing } protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException { return true; // always connect } } static public class FooAddress extends Address { public FooAddress() { } public String getType() { return "foo"; } public String toString() { return "yada"; } public boolean equals(Object other) { return true; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/PasswordAuthenticationTest.java0000664000175000017500000000316110517560657030267 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class PasswordAuthenticationTest extends TestCase { public PasswordAuthenticationTest(String name) { super(name); } public void testPA() { String user = String.valueOf(System.currentTimeMillis()); String password = "JobbyJobbyJobby" + user; PasswordAuthentication pa = new PasswordAuthentication(user, password); assertEquals(user, pa.getUserName()); assertEquals(password, pa.getPassword()); } public void testPasswordAuthentication() { PasswordAuthentication pa = new PasswordAuthentication("Alex", "xelA"); assertEquals("Alex", pa.getUserName()); assertEquals("xelA", pa.getPassword()); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/0000775000175000017500000000000011703375727023202 5ustar brianbriangeronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/TransportEventTest.java0000664000175000017500000000610110517560657027701 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Address; import javax.mail.Folder; import javax.mail.Message; import javax.mail.TestData; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class TransportEventTest extends TestCase { public TransportEventTest(String name) { super(name); } public void testEvent() throws AddressException { doEventTests(TransportEvent.MESSAGE_DELIVERED); doEventTests(TransportEvent.MESSAGE_PARTIALLY_DELIVERED); doEventTests(TransportEvent.MESSAGE_NOT_DELIVERED); } private void doEventTests(int type) throws AddressException { Folder folder = TestData.getTestFolder(); Message message = TestData.getMessage(); Transport transport = TestData.getTestTransport(); Address[] sent = new Address[] { new InternetAddress("alex@here.com")}; Address[] empty = new Address[0]; TransportEvent event = new TransportEvent(transport, type, sent, empty, empty, message); assertEquals(transport, event.getSource()); assertEquals(type, event.getType()); TransportListenerTest listener = new TransportListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class TransportListenerTest implements TransportListener { private int state = 0; public void messageDelivered(TransportEvent event) { if (state != 0) { fail("Recycled Listener"); } state = TransportEvent.MESSAGE_DELIVERED; } public void messagePartiallyDelivered(TransportEvent event) { if (state != 0) { fail("Recycled Listener"); } state = TransportEvent.MESSAGE_PARTIALLY_DELIVERED; } public void messageNotDelivered(TransportEvent event) { if (state != 0) { fail("Recycled Listener"); } state = TransportEvent.MESSAGE_NOT_DELIVERED; } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/MessageCountEventTest.java0000664000175000017500000000474310517560657030314 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Folder; import javax.mail.TestData; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class MessageCountEventTest extends TestCase { public MessageCountEventTest(String name) { super(name); } public void testEvent() { doEventTests(MessageCountEvent.ADDED); doEventTests(MessageCountEvent.REMOVED); try { doEventTests(-12345); fail("Expected exception due to invalid type -12345"); } catch (IllegalArgumentException e) { } } private void doEventTests(int type) { Folder folder = TestData.getTestFolder(); MessageCountEvent event = new MessageCountEvent(folder, type, false, null); assertEquals(folder, event.getSource()); assertEquals(type, event.getType()); MessageCountListenerTest listener = new MessageCountListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class MessageCountListenerTest implements MessageCountListener { private int state = 0; public void messagesAdded(MessageCountEvent event) { if (state != 0) { fail("Recycled Listener"); } state = MessageCountEvent.ADDED; } public void messagesRemoved(MessageCountEvent event) { if (state != 0) { fail("Recycled Listener"); } state = MessageCountEvent.REMOVED; } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/AllEventTests.java0000664000175000017500000000305610517560657026606 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import junit.framework.Test; import junit.framework.TestSuite; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class AllEventTests { public static Test suite() { TestSuite suite = new TestSuite("Test for javax.mail.event"); //$JUnit-BEGIN$ suite.addTest(new TestSuite(ConnectionEventTest.class)); suite.addTest(new TestSuite(FolderEventTest.class)); suite.addTest(new TestSuite(MessageChangedEventTest.class)); suite.addTest(new TestSuite(StoreEventTest.class)); suite.addTest(new TestSuite(MessageCountEventTest.class)); suite.addTest(new TestSuite(TransportEventTest.class)); //$JUnit-END$ return suite; } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/ConnectionEventTest.java0000664000175000017500000000463510517560657030016 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class ConnectionEventTest extends TestCase { public static class ConnectionListenerTest implements ConnectionListener { private int state = 0; public void closed(ConnectionEvent event) { if (state != 0) { fail("Recycled ConnectionListener"); } state = ConnectionEvent.CLOSED; } public void disconnected(ConnectionEvent event) { if (state != 0) { fail("Recycled ConnectionListener"); } state = ConnectionEvent.DISCONNECTED; } public int getState() { return state; } public void opened(ConnectionEvent event) { if (state != 0) { fail("Recycled ConnectionListener"); } state = ConnectionEvent.OPENED; } } public ConnectionEventTest(String name) { super(name); } private void doEventTests(int type) { ConnectionEvent event = new ConnectionEvent(this, type); assertEquals(this, event.getSource()); assertEquals(type, event.getType()); ConnectionListenerTest listener = new ConnectionListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public void testEvent() { doEventTests(ConnectionEvent.CLOSED); doEventTests(ConnectionEvent.OPENED); doEventTests(ConnectionEvent.DISCONNECTED); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/FolderEventTest.java0000664000175000017500000000451210517560657027124 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class FolderEventTest extends TestCase { public FolderEventTest(String name) { super(name); } public void testEvent() { doEventTests(FolderEvent.CREATED); doEventTests(FolderEvent.RENAMED); doEventTests(FolderEvent.DELETED); } private void doEventTests(int type) { FolderEvent event = new FolderEvent(this, null, type); assertEquals(this, event.getSource()); assertEquals(type, event.getType()); FolderListenerTest listener = new FolderListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class FolderListenerTest implements FolderListener { private int state = 0; public void folderCreated(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.CREATED; } public void folderDeleted(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.DELETED; } public void folderRenamed(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.RENAMED; } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/StoreEventTest.java0000664000175000017500000000452110517560657027005 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import javax.mail.Store; import javax.mail.TestData; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class StoreEventTest extends TestCase { public StoreEventTest(String name) { super(name); } public void testEvent() { doEventTests(StoreEvent.ALERT); doEventTests(StoreEvent.NOTICE); try { StoreEvent event = new StoreEvent(null, -12345, "Hello World"); fail( "Expected exception due to invalid type " + event.getMessageType()); } catch (IllegalArgumentException e) { } } private void doEventTests(int type) { Store source = TestData.getTestStore(); StoreEvent event = new StoreEvent(source, type, "Hello World"); assertEquals(source, event.getSource()); assertEquals("Hello World", event.getMessage()); assertEquals(type, event.getMessageType()); StoreListenerTest listener = new StoreListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class StoreListenerTest implements StoreListener { private int state = 0; public void notification(StoreEvent event) { if (state != 0) { fail("Recycled Listener"); } state = event.getMessageType(); } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/event/MessageChangedEventTest.java0000664000175000017500000000404310517560657030546 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail.event; import junit.framework.TestCase; /** * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class MessageChangedEventTest extends TestCase { public MessageChangedEventTest(String name) { super(name); } public void testEvent() { doEventTests(MessageChangedEvent.ENVELOPE_CHANGED); doEventTests(MessageChangedEvent.FLAGS_CHANGED); } private void doEventTests(int type) { MessageChangedEvent event = new MessageChangedEvent(this, type, null); assertEquals(this, event.getSource()); assertEquals(type, event.getMessageChangeType()); MessageChangedListenerTest listener = new MessageChangedListenerTest(); event.dispatch(listener); assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class MessageChangedListenerTest implements MessageChangedListener { private int state = 0; public void messageChanged(MessageChangedEvent event) { if (state != 0) { fail("Recycled Listener"); } state = event.getMessageChangeType(); } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/MessagingExceptionTest.java0000664000175000017500000000523010517560657027360 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import junit.framework.TestCase; /** * @version $Revision $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ */ public class MessagingExceptionTest extends TestCase { private RuntimeException d; private MessagingException c; private MessagingException b; private MessagingException a; public MessagingExceptionTest(String name) { super(name); } protected void setUp() throws Exception { super.setUp(); a = new MessagingException("A"); b = new MessagingException("B"); c = new MessagingException("C"); d = new RuntimeException("D"); } public void testMessagingExceptionString() { assertEquals("A", a.getMessage()); } public void testNextException() { assertTrue(a.setNextException(b)); assertEquals(b, a.getNextException()); assertTrue(a.setNextException(c)); assertEquals(b, a.getNextException()); assertEquals(c, b.getNextException()); String message = a.getMessage(); int ap = message.indexOf("A"); int bp = message.indexOf("B"); int cp = message.indexOf("C"); assertTrue("A does not contain 'A'", ap != -1); assertTrue("B does not contain 'B'", bp != -1); assertTrue("C does not contain 'C'", cp != -1); } public void testNextExceptionWrong() { assertTrue(a.setNextException(d)); assertFalse(a.setNextException(b)); } public void testNextExceptionWrong2() { assertTrue(a.setNextException(d)); assertFalse(a.setNextException(b)); } public void testMessagingExceptionStringException() { MessagingException x = new MessagingException("X", a); assertEquals("X (javax.mail.MessagingException: A)", x.getMessage()); assertEquals(a, x.getNextException()); assertEquals(a, x.getCause()); } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/EventQueueTest.java0000664000175000017500000000611110702410673025635 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.util.Vector; import javax.mail.MessagingException; import javax.mail.event.FolderEvent; import javax.mail.event.FolderListener; import junit.framework.TestCase; /** * @version $Rev: 582780 $ $Date: 2007-10-08 07:17:15 -0400 (Mon, 08 Oct 2007) $ */ public class EventQueueTest extends TestCase { protected EventQueue queue; public void setUp() throws Exception { queue = new EventQueue(); } public void tearDown() throws Exception { queue.stop(); } public void testEvent() { doEventTests(FolderEvent.CREATED); doEventTests(FolderEvent.RENAMED); doEventTests(FolderEvent.DELETED); } private void doEventTests(int type) { // These tests are essentially the same as the // folder event tests, but done using the asynchronous // event queue. FolderEvent event = new FolderEvent(this, null, type); assertEquals(this, event.getSource()); assertEquals(type, event.getType()); FolderListenerTest listener = new FolderListenerTest(); Vector listeners = new Vector(); listeners.add(listener); queue.queueEvent(event, listeners); // we need to make sure the queue thread has a chance to dispatch // this before we check. try { Thread.currentThread().sleep(1000); } catch (InterruptedException e ) { } assertEquals("Unexpcted method dispatched", type, listener.getState()); } public static class FolderListenerTest implements FolderListener { private int state = 0; public void folderCreated(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.CREATED; } public void folderDeleted(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.DELETED; } public void folderRenamed(FolderEvent event) { if (state != 0) { fail("Recycled Listener"); } state = FolderEvent.RENAMED; } public int getState() { return state; } } } geronimo-javamail-1.4-spec-1.7.1/src/test/java/javax/mail/URLNameTest.java0000664000175000017500000003474310714667425025042 0ustar brianbrian/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package javax.mail; import java.net.MalformedURLException; import java.net.URL; import junit.framework.TestCase; /** * @version $Rev: 593290 $ $Date: 2007-11-08 15:18:29 -0500 (Thu, 08 Nov 2007) $ */ public class URLNameTest extends TestCase { public URLNameTest(String name) { super(name); } public void testURLNameString() { String s; URLName name; s = "http://www.apache.org"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "http://www.apache.org/file/file1#ref"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file1", name.getFile()); assertEquals("ref", name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "http://www.apache.org/file/"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/", name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "http://john@www.apache.org/file/"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/", name.getFile()); assertNull(name.getRef()); assertEquals("john", name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "http://john:doe@www.apache.org/file/"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/", name.getFile()); assertNull(name.getRef()); assertEquals("john", name.getUsername()); assertEquals("doe", name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "http://john%40gmail.com:doe@www.apache.org/file/"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/", name.getFile()); assertNull(name.getRef()); assertEquals("john@gmail.com", name.getUsername()); assertEquals("doe", name.getPassword()); try { assertEquals(new URL(s), name.getURL()); } catch (MalformedURLException e) { fail(); } s = "file/file2"; name = new URLName(s); assertNull(name.getProtocol()); assertNull(name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { name.getURL(); fail(); } catch (MalformedURLException e) { // OK } name = new URLName((String) null); assertNull( name.getProtocol()); assertNull(name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { name.getURL(); fail(); } catch (MalformedURLException e) { // OK } name = new URLName(""); assertNull( name.getProtocol()); assertNull(name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { name.getURL(); fail(); } catch (MalformedURLException e) { // OK } } public void testURLNameAll() { URLName name; name = new URLName(null, null, -1, null, null, null); assertNull(name.getProtocol()); assertNull(name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { name.getURL(); fail(); } catch (MalformedURLException e) { // OK } name = new URLName("", "", -1, "", "", ""); assertNull(name.getProtocol()); assertNull(name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { name.getURL(); fail(); } catch (MalformedURLException e) { // OK } name = new URLName("http", "www.apache.org", -1, null, null, null); assertEquals("http://www.apache.org", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL("http://www.apache.org"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", 8080, "", "", ""); assertEquals("http://www.apache.org:8080", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(8080, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL("http://www.apache.org:8080"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", -1, "file/file2", "", ""); assertEquals("http://www.apache.org/file/file2", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL("http://www.apache.org/file/file2"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", -1, "file/file2", "john", ""); assertEquals("http://john@www.apache.org/file/file2", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertEquals("john", name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL("http://john@www.apache.org/file/file2"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", -1, "file/file2", "john", "doe"); assertEquals("http://john:doe@www.apache.org/file/file2", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertEquals("john", name.getUsername()); assertEquals("doe", name.getPassword()); try { assertEquals(new URL("http://john:doe@www.apache.org/file/file2"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", -1, "file/file2", "john@gmail.com", "doe"); assertEquals("http://john%40gmail.com:doe@www.apache.org/file/file2", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertEquals("john@gmail.com", name.getUsername()); assertEquals("doe", name.getPassword()); try { assertEquals(new URL("http://john%40gmail.com:doe@www.apache.org/file/file2"), name.getURL()); } catch (MalformedURLException e) { fail(); } name = new URLName("http", "www.apache.org", -1, "file/file2", "", "doe"); assertEquals("http://www.apache.org/file/file2", name.toString()); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("file/file2", name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(new URL("http://www.apache.org/file/file2"), name.getURL()); } catch (MalformedURLException e) { fail(); } } public void testURLNameURL() throws MalformedURLException { URL url; URLName name; url = new URL("http://www.apache.org"); name = new URLName(url); assertEquals("http", name.getProtocol()); assertEquals("www.apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertNull(name.getFile()); assertNull(name.getRef()); assertNull(name.getUsername()); assertNull(name.getPassword()); try { assertEquals(url, name.getURL()); } catch (MalformedURLException e) { fail(); } } public void testEquals() throws MalformedURLException { URLName name1 = new URLName("http://www.apache.org"); assertEquals(name1, new URLName("http://www.apache.org")); assertEquals(name1, new URLName(new URL("http://www.apache.org"))); assertEquals(name1, new URLName("http", "www.apache.org", -1, null, null, null)); assertEquals(name1, new URLName("http://www.apache.org#foo")); // wierd but ref is not part of the equals contract assertTrue(!name1.equals(new URLName("http://www.apache.org:8080"))); assertTrue(!name1.equals(new URLName("http://cvs.apache.org"))); assertTrue(!name1.equals(new URLName("https://www.apache.org"))); name1 = new URLName("http://john:doe@www.apache.org"); assertEquals(name1, new URLName(new URL("http://john:doe@www.apache.org"))); assertEquals(name1, new URLName("http", "www.apache.org", -1, null, "john", "doe")); assertTrue(!name1.equals(new URLName("http://john:xxx@www.apache.org"))); assertTrue(!name1.equals(new URLName("http://xxx:doe@www.apache.org"))); assertTrue(!name1.equals(new URLName("http://www.apache.org"))); assertEquals(new URLName("http://john@www.apache.org"), new URLName("http", "www.apache.org", -1, null, "john", null)); assertEquals(new URLName("http://www.apache.org"), new URLName("http", "www.apache.org", -1, null, null, "doe")); } public void testHashCode() { URLName name1 = new URLName("http://www.apache.org/file"); URLName name2 = new URLName("http://www.apache.org/file#ref"); assertTrue(name1.equals(name2)); assertTrue(name1.hashCode() == name2.hashCode()); } public void testNullProtocol() { URLName name1 = new URLName(null, "www.apache.org", -1, null, null, null); assertTrue(!name1.equals(name1)); } public void testOpaqueSchemes() { String s; URLName name; // not strictly opaque but no protocol handler installed s = "foo://jdoe@apache.org/INBOX"; name = new URLName(s); assertEquals(s, name.toString()); assertEquals("foo", name.getProtocol()); assertEquals("apache.org", name.getHost()); assertEquals(-1, name.getPort()); assertEquals("INBOX", name.getFile()); assertNull(name.getRef()); assertEquals("jdoe", name.getUsername()); assertNull(name.getPassword()); // TBD as I am not sure what other URL formats to use } } geronimo-javamail-1.4-spec-1.7.1/LICENSE0000664000175000017500000003406611117313275016342 0ustar brianbrian Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ######################################################################### ## ADDITIONAL LICENSES ## ######################################################################### The XMLSchema.dtd included in this project was developed by the W3C Consortium (http://www.w3c.org/). Use of the source code, thus licensed, and the resultant binary are subject to the terms and conditions of the following license. W3C¨ SOFTWARE NOTICE AND LICENSE Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: 1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. 2. Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" 3. Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.) THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders. geronimo-javamail-1.4-spec-1.7.1/README.txt0000664000175000017500000000374710474162255017042 0ustar brianbrianJavaMail Specification 1.4 ========================== These classes are based on the JavaMail Specification taken from J2EE 1.4 API documentation and on the JavaMail 1.3 specification PDF. The classes represent the JavaMail API and contain implementations of the classes found in the javax.mail packages. In order to function correctly, these classes require: o The Java Activation Framework (JAF) in javax.activation o Java 2 (Java 1.2 or later) o Implementations of the JavaMail providers to deal with pop/imap/etc. (You may use this with the geronimo-mail implementation or write your own) This contains no JavaDoc: see http://java.sun.com/products/javamail/ http://java.sun.com/j2ee/1.4/docs/api/ for more information on how to use JavaMail to send messages. Configuration ------------- The JavaMail spec defines the following configuration files: javamail.providers [Defines which classes are used to map to protocols] javamail.address.map [Defines which message types (rfc822, news) map to protocols] They need to be in the CLASSPATH (or in a Jar) in a directory /META-INF/ e.g. c:\mymail\META-INF\javamail.providers Providers --------- Provides a protocol, along with its implementation and whether it is a store or a transport (subclass of javax.mail.Store or javax.mail.Transport) protocol=smtp;type=transport;class=org.me.MySMTPTransport;vendor=Me Inc;version=1.0 protocol=imap;type=store;class=org.me.MyIMAPStore;vendor=Me Inc;version=1.0 Address Map ----------- Contains entries in 'name=value' format: rfc822=smtp news=nntp Default ======= To ensure that other files can be extended at a later stage, the JavaMail spec defines three locations for these files: $JAVA_HOME\lib\javamail.properties META-INF\javamail.properties META-INF\javamail.default.properties The files are located in that order and overwrite whatever the previous version contained, so if 'smtp' is defined in javamail.properties and javamail.default.properties, it will use the one from javamail.default.properties geronimo-javamail-1.4-spec-1.7.1/NOTICE0000664000175000017500000000025111403457574016237 0ustar brianbrianApache Geronimo Copyright 2003-2010 The Apache Software Foundation This product includes software developed by The Apache Software Foundation (http://www.apache.org/).