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