001    /**
002     *
003     * Copyright 2003-2006 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package javax.mail;
019    
020    import java.io.InvalidObjectException;
021    import java.io.ObjectStreamException;
022    import java.io.Serializable;
023    import java.util.Date;
024    import javax.mail.search.SearchTerm;
025    
026    /**
027     * @version $Rev: 421852 $ $Date: 2006-07-14 03:02:19 -0700 (Fri, 14 Jul 2006) $
028     */
029    public abstract class Message implements Part {
030        /**
031         * Enumeration of types of recipients allowed by the Message class.
032         */
033        public static class RecipientType implements Serializable {
034            /**
035             * A "To" or primary recipient.
036             */
037            public static final RecipientType TO = new RecipientType("To");
038            /**
039             * A "Cc" or carbon-copy recipient.
040             */
041            public static final RecipientType CC = new RecipientType("Cc");
042            /**
043             * A "Bcc" or blind carbon-copy recipient.
044             */
045            public static final RecipientType BCC = new RecipientType("Bcc");
046            protected String type;
047    
048            protected RecipientType(String type) {
049                this.type = type;
050            }
051    
052            protected Object readResolve() throws ObjectStreamException {
053                if (type.equals("To")) {
054                    return TO;
055                } else if (type.equals("Cc")) {
056                    return CC;
057                } else if (type.equals("Bcc")) {
058                    return BCC;
059                } else {
060                    throw new InvalidObjectException("Invalid RecipientType: " + type);
061                }
062            }
063    
064            public String toString() {
065                return type;
066            }
067        }
068    
069        /**
070         * The index of a message withing its folder, or zero if the message was not retrieved from a folder.
071         */
072        protected int msgnum;
073        /**
074         * True if this message has been expunged from the Store.
075         */
076        protected boolean expunged;
077        /**
078         * The {@link Folder} that contains this message, or null if it was not obtained from a folder.
079         */
080        protected Folder folder;
081        /**
082         * The {@link Session} associated with this message.
083         */
084        protected Session session;
085    
086        /**
087         * Default constructor.
088         */
089        protected Message() {
090        }
091    
092        /**
093         * Constructor initializing folder and message msgnum; intended to be used by implementations of Folder.
094         *
095         * @param folder the folder that contains the message
096         * @param msgnum the message index within the folder
097         */
098        protected Message(Folder folder, int msgnum) {
099            this.folder = folder;
100            this.msgnum = msgnum;
101            // make sure we copy the session information from the folder.
102            this.session = folder.getStore().getSession();
103        }
104    
105        /**
106         * Constructor initializing the session; intended to by used by client created instances.
107         *
108         * @param session the session associated with this message
109         */
110        protected Message(Session session) {
111            this.session = session;
112        }
113    
114        /**
115         * Return the "From" header indicating the identity of the person who the message is from;
116         * in some circumstances this may be different to the actual sender.
117         *
118         * @return a list of addresses this message is from; may be empty if the header is present but empty, or null
119         *         if the header is not present
120         * @throws MessagingException if there was a problem accessing the store
121         */
122        public abstract Address[] getFrom() throws MessagingException;
123    
124        /**
125         * Set the "From" header for this message to the value of the "mail.user" property,
126         * of if that property is not set, to the value of the system property "user.name"
127         *
128         * @throws MessagingException if there was a problem accessing the store
129         */
130        public abstract void setFrom() throws MessagingException;
131    
132        /**
133         * Set the "From" header to the supplied address.
134         *
135         * @param address the address of the person who the message is from
136         * @throws MessagingException if there was a problem accessing the store
137         */
138        public abstract void setFrom(Address address) throws MessagingException;
139    
140        /**
141         * Add multiple addresses to the "From" header.
142         *
143         * @param addresses the addresses to add
144         * @throws MessagingException if there was a problem accessing the store
145         */
146        public abstract void addFrom(Address[] addresses) throws MessagingException;
147    
148        /**
149         * Get all recipients of the given type.
150         *
151         * @param type the type of recipient to get
152         * @return a list of addresses; may be empty if the header is present but empty,
153         *         or null if the header is not present
154         * @throws MessagingException if there was a problem accessing the store
155         * @see RecipientType
156         */
157        public abstract Address[] getRecipients(RecipientType type) throws MessagingException;
158    
159        /**
160         * Get all recipients of this message.
161         * The default implementation extracts the To, Cc, and Bcc recipients using {@link #getRecipients(javax.mail.Message.RecipientType)}
162         * and then concatentates the results into a single array; it returns null if no headers are defined.
163         *
164         * @return an array containing all recipients
165         * @throws MessagingException if there was a problem accessing the store
166         */
167        public Address[] getAllRecipients() throws MessagingException {
168            Address[] to = getRecipients(RecipientType.TO);
169            Address[] cc = getRecipients(RecipientType.CC);
170            Address[] bcc = getRecipients(RecipientType.BCC);
171            if (to == null && cc == null && bcc == null) {
172                return null;
173            }
174            int length = (to != null ? to.length : 0) + (cc != null ? cc.length : 0) + (bcc != null ? bcc.length : 0);
175            Address[] result = new Address[length];
176            int j = 0;
177            if (to != null) {
178                for (int i = 0; i < to.length; i++) {
179                    result[j++] = to[i];
180                }
181            }
182            if (cc != null) {
183                for (int i = 0; i < cc.length; i++) {
184                    result[j++] = cc[i];
185                }
186            }
187            if (bcc != null) {
188                for (int i = 0; i < bcc.length; i++) {
189                    result[j++] = bcc[i];
190                }
191            }
192            return result;
193        }
194    
195        /**
196         * Set the list of recipients for the specified type.
197         *
198         * @param type      the type of recipient
199         * @param addresses the new addresses
200         * @throws MessagingException if there was a problem accessing the store
201         */
202        public abstract void setRecipients(RecipientType type, Address[] addresses) throws MessagingException;
203    
204        /**
205         * Set the list of recipients for the specified type to a single address.
206         *
207         * @param type    the type of recipient
208         * @param address the new address
209         * @throws MessagingException if there was a problem accessing the store
210         */
211        public void setRecipient(RecipientType type, Address address) throws MessagingException {
212            setRecipients(type, new Address[]{address});
213        }
214    
215        /**
216         * Add recipents of a specified type.
217         *
218         * @param type      the type of recipient
219         * @param addresses the addresses to add
220         * @throws MessagingException if there was a problem accessing the store
221         */
222        public abstract void addRecipients(RecipientType type, Address[] addresses) throws MessagingException;
223    
224        /**
225         * Add a recipent of a specified type.
226         *
227         * @param type    the type of recipient
228         * @param address the address to add
229         * @throws MessagingException if there was a problem accessing the store
230         */
231        public void addRecipient(RecipientType type, Address address) throws MessagingException {
232            addRecipients(type, new Address[]{address});
233        }
234    
235        /**
236         * Get the addresses to which replies should be directed.
237         * <p/>
238         * As the most common behavior is to return to sender, the default implementation
239         * simply calls {@link #getFrom()}.
240         *
241         * @return a list of addresses to which replies should be directed
242         * @throws MessagingException if there was a problem accessing the store
243         */
244        public Address[] getReplyTo() throws MessagingException {
245            return getFrom();
246        }
247    
248        /**
249         * Set the addresses to which replies should be directed.
250         * <p/>
251         * The default implementation throws a MethodNotSupportedException.
252         *
253         * @param addresses to which replies should be directed
254         * @throws MessagingException if there was a problem accessing the store
255         */
256        public void setReplyTo(Address[] addresses) throws MessagingException {
257            throw new MethodNotSupportedException("setReplyTo not supported");
258        }
259    
260        /**
261         * Get the subject for this message.
262         *
263         * @return the subject
264         * @throws MessagingException if there was a problem accessing the store
265         */
266        public abstract String getSubject() throws MessagingException;
267    
268        /**
269         * Set the subject of this message
270         *
271         * @param subject the subject
272         * @throws MessagingException if there was a problem accessing the store
273         */
274        public abstract void setSubject(String subject) throws MessagingException;
275    
276        /**
277         * Return the date that this message was sent.
278         *
279         * @return the date this message was sent
280         * @throws MessagingException if there was a problem accessing the store
281         */
282        public abstract Date getSentDate() throws MessagingException;
283    
284        /**
285         * Set the date this message was sent.
286         *
287         * @param sent the date when this message was sent
288         * @throws MessagingException if there was a problem accessing the store
289         */
290        public abstract void setSentDate(Date sent) throws MessagingException;
291    
292        /**
293         * Return the date this message was received.
294         *
295         * @return the date this message was received
296         * @throws MessagingException if there was a problem accessing the store
297         */
298        public abstract Date getReceivedDate() throws MessagingException;
299    
300        /**
301         * Return a copy the flags associated with this message.
302         *
303         * @return a copy of the flags for this message
304         * @throws MessagingException if there was a problem accessing the store
305         */
306        public abstract Flags getFlags() throws MessagingException;
307    
308        /**
309         * Check whether the supplied flag is set.
310         * The default implementation checks the flags returned by {@link #getFlags()}.
311         *
312         * @param flag the flags to check for
313         * @return true if the flags is set
314         * @throws MessagingException if there was a problem accessing the store
315         */
316        public boolean isSet(Flags.Flag flag) throws MessagingException {
317            return getFlags().contains(flag);
318        }
319    
320        /**
321         * Set the flags specified to the supplied value; flags not included in the
322         * supplied {@link Flags} parameter are not affected.
323         *
324         * @param flags the flags to modify
325         * @param set   the new value of those flags
326         * @throws MessagingException if there was a problem accessing the store
327         */
328        public abstract void setFlags(Flags flags, boolean set) throws MessagingException;
329    
330        /**
331         * Set a flag to the supplied value.
332         * The default implmentation uses {@link #setFlags(Flags, boolean)}.
333         *
334         * @param flag the flag to set
335         * @param set  the value for that flag
336         * @throws MessagingException if there was a problem accessing the store
337         */
338        public void setFlag(Flags.Flag flag, boolean set) throws MessagingException {
339            setFlags(new Flags(flag), set);
340        }
341    
342        /**
343         * Return the message number for this Message.
344         * This number refers to the relative position of this message in a Folder; the message
345         * number for any given message can change during a seesion if the Folder is expunged.
346         * Message numbers for messages in a folder start at one; the value zero indicates that
347         * this message does not belong to a folder.
348         *
349         * @return the message number
350         */
351        public int getMessageNumber() {
352            return msgnum;
353        }
354    
355        /**
356         * Set the message number for this Message.
357         * This must be invoked by implementation classes when the message number changes.
358         *
359         * @param number the new message number
360         */
361        protected void setMessageNumber(int number) {
362            msgnum = number;
363        }
364    
365        /**
366         * Return the folder containing this message. If this is a new or nested message
367         * then this method returns null.
368         *
369         * @return the folder containing this message
370         */
371        public Folder getFolder() {
372            return folder;
373        }
374    
375        /**
376         * Checks to see if this message has been expunged. If true, all methods other than
377         * {@link #getMessageNumber()} are invalid.
378         *
379         * @return true if this method has been expunged
380         */
381        public boolean isExpunged() {
382            return expunged;
383        }
384    
385        /**
386         * Set the expunged flag for this message.
387         *
388         * @param expunged true if this message has been expunged
389         */
390        protected void setExpunged(boolean expunged) {
391            this.expunged = expunged;
392        }
393    
394        /**
395         * Create a new message suitable as a reply to this message with all headers set
396         * up appropriately. The message body will be empty.
397         * <p/>
398         * if replyToAll is set then the new message will be addressed to all recipients
399         * of this message; otherwise the reply will be addressed only to the sender as
400         * returned by {@link #getReplyTo()}.
401         * <p/>
402         * The subject field will be initialized with the subject field from the orginal
403         * message; the text "Re:" will be prepended unless it is already present.
404         *
405         * @param replyToAll if true, indciates the message should be addressed to all recipients not just the sender
406         * @return a new message suitable as a reply to this message
407         * @throws MessagingException if there was a problem accessing the store
408         */
409        public abstract Message reply(boolean replyToAll) throws MessagingException;
410    
411        /**
412         * To ensure changes are saved to the store, this message should be invoked
413         * before its containing folder is closed. Implementations may save modifications
414         * immediately but are free to defer such updates to they may be sent to the server
415         * in one batch; if saveChanges is not called then such changes may not be made
416         * permanent.
417         *
418         * @throws MessagingException if there was a problem accessing the store
419         */
420        public abstract void saveChanges() throws MessagingException;
421    
422        /**
423         * Apply the specified search criteria to this message
424         *
425         * @param term the search criteria
426         * @return true if this message matches the search criteria.
427         * @throws MessagingException if there was a problem accessing the store
428         */
429        public boolean match(SearchTerm term) throws MessagingException {
430            return term.match(this);
431        }
432    }