123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- /* ====================================================================
- 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.poi.hsmf;
-
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Calendar;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.apache.poi.POIReadOnlyDocument;
- import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
- import org.apache.poi.hsmf.datatypes.AttachmentChunks;
- import org.apache.poi.hsmf.datatypes.AttachmentChunks.AttachmentChunksSorter;
- import org.apache.poi.hsmf.datatypes.ByteChunk;
- import org.apache.poi.hsmf.datatypes.Chunk;
- import org.apache.poi.hsmf.datatypes.ChunkGroup;
- import org.apache.poi.hsmf.datatypes.Chunks;
- import org.apache.poi.hsmf.datatypes.MAPIProperty;
- import org.apache.poi.hsmf.datatypes.NameIdChunks;
- import org.apache.poi.hsmf.datatypes.PropertyValue;
- import org.apache.poi.hsmf.datatypes.PropertyValue.LongPropertyValue;
- import org.apache.poi.hsmf.datatypes.PropertyValue.TimePropertyValue;
- import org.apache.poi.hsmf.datatypes.RecipientChunks;
- import org.apache.poi.hsmf.datatypes.RecipientChunks.RecipientChunksSorter;
- import org.apache.poi.hsmf.datatypes.StringChunk;
- import org.apache.poi.hsmf.datatypes.Types;
- import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
- import org.apache.poi.hsmf.parsers.POIFSChunkParser;
- import org.apache.poi.poifs.filesystem.DirectoryNode;
- import org.apache.poi.poifs.filesystem.POIFSFileSystem;
- import org.apache.poi.util.CodePageUtil;
- import org.apache.poi.util.LocaleUtil;
-
- import static org.apache.logging.log4j.util.Unbox.box;
-
- /**
- * Reads an Outlook MSG File in and provides hooks into its data structure.
- *
- * If you want to develop with HSMF, you might find it worth getting
- * some of the Microsoft public documentation, such as:
- *
- * [MS-OXCMSG]: Message and Attachment Object Protocol Specification
- */
- public class MAPIMessage extends POIReadOnlyDocument {
-
- /**
- * A MAPI file can be an email (NOTE) or a number of other types
- */
- public enum MESSAGE_CLASS {
- APPOINTMENT,
- CONTACT,
- NOTE,
- POST,
- STICKY_NOTE,
- TASK,
- UNKNOWN,
- UNSPECIFIED
- }
-
- /** For logging problems we spot with the file */
- private static final Logger LOG = LogManager.getLogger(MAPIMessage.class);
-
- private Chunks mainChunks;
- private NameIdChunks nameIdChunks;
- private RecipientChunks[] recipientChunks;
- private AttachmentChunks[] attachmentChunks;
-
- private boolean returnNullOnMissingChunk;
-
- /**
- * Constructor for creating new files.
- */
- public MAPIMessage() {
- // TODO - make writing possible
- super(new POIFSFileSystem());
- }
-
-
- /**
- * Constructor for reading MSG Files from the file system.
- *
- * @param filename Name of the file to read
- * @throws IOException on errors reading, or invalid data
- * @throws RuntimeException a number of runtime exceptions can be thrown, especially if there are problems with the
- * input format
- */
- public MAPIMessage(String filename) throws IOException {
- this(new File(filename));
- }
- /**
- * Constructor for reading MSG Files from the file system.
- *
- * @param file The file to read from
- * @throws IOException on errors reading, or invalid data
- * @throws RuntimeException a number of runtime exceptions can be thrown, especially if there are problems with the
- * input format
- */
- public MAPIMessage(File file) throws IOException {
- this(new POIFSFileSystem(file));
- }
-
- /**
- * Constructor for reading MSG Files from an input stream.
- *
- * <p>Note - this will buffer the whole message into memory
- * in order to process. For lower memory use, use {@link #MAPIMessage(File)}
- *
- * @param in The InputStream to buffer and then read from
- * @throws IOException on errors reading, or invalid data
- * @throws RuntimeException a number of runtime exceptions can be thrown, especially if there are problems with the
- * input format
- */
- public MAPIMessage(InputStream in) throws IOException {
- this(new POIFSFileSystem(in));
- }
- /**
- * Constructor for reading MSG Files from a POIFS filesystem
- *
- * @param fs Open POIFS FileSystem containing the message
- * @throws IOException on errors reading, or invalid data
- * @throws RuntimeException a number of runtime exceptions can be thrown, especially if there are problems with the
- * input format
- */
- public MAPIMessage(POIFSFileSystem fs) throws IOException {
- this(fs.getRoot());
- }
- /**
- * Constructor for reading MSG Files from a certain
- * point within a POIFS filesystem
- * @param poifsDir Directory containing the message
- * @throws IOException on errors reading, or invalid data
- * @throws RuntimeException a number of runtime exceptions can be thrown, especially if there are problems with the
- * input format
- */
- public MAPIMessage(DirectoryNode poifsDir) throws IOException {
- super(poifsDir);
-
- // Grab all the chunks
- ChunkGroup[] chunkGroups = POIFSChunkParser.parse(poifsDir);
-
- // Grab interesting bits
- ArrayList<AttachmentChunks> attachments = new ArrayList<>();
- ArrayList<RecipientChunks> recipients = new ArrayList<>();
- for(ChunkGroup group : chunkGroups) {
- // Should only ever be one of each of these
- if(group instanceof Chunks) {
- mainChunks = (Chunks)group;
- } else if(group instanceof NameIdChunks) {
- nameIdChunks = (NameIdChunks)group;
- } else if(group instanceof RecipientChunks) {
- recipients.add( (RecipientChunks)group );
- }
-
- // Can be multiple of these - add to list(s)
- if(group instanceof AttachmentChunks) {
- attachments.add( (AttachmentChunks)group );
- }
- }
- attachmentChunks = attachments.toArray(new AttachmentChunks[0]);
- recipientChunks = recipients.toArray(new RecipientChunks[0]);
-
- // Now sort these chunks lists so they're in ascending order,
- // rather than in random filesystem order
- Arrays.sort(attachmentChunks, new AttachmentChunksSorter());
- Arrays.sort(recipientChunks, new RecipientChunksSorter());
- }
-
-
- /**
- * Gets a string value based on the passed chunk.
- * @throws ChunkNotFoundException if the chunk isn't there
- */
- public String getStringFromChunk(StringChunk chunk) throws ChunkNotFoundException {
- if(chunk == null) {
- if(returnNullOnMissingChunk) {
- return null;
- } else {
- throw new ChunkNotFoundException();
- }
- }
- return chunk.getValue();
- }
-
-
- /**
- * Gets the plain text body of this Outlook Message
- * @return The string representation of the 'text' version of the body, if available.
- * @throws ChunkNotFoundException If the text-body chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getTextBody() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getTextBodyChunk());
- }
-
- /**
- * Gets the html body of this Outlook Message, if this email
- * contains a html version.
- * @return The string representation of the 'html' version of the body, if available.
- * @throws ChunkNotFoundException If the html-body chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getHtmlBody() throws ChunkNotFoundException {
- ByteChunk htmlBodyBinaryChunk = mainChunks.getHtmlBodyChunkBinary();
- if (htmlBodyBinaryChunk != null) {
- List<PropertyValue> cpid = mainChunks.getProperties().get(MAPIProperty.INTERNET_CPID);
- if (cpid != null && cpid.size() > 0) {
- int codepage = ((LongPropertyValue) cpid.get(0)).getValue();
- try {
- String encoding = CodePageUtil.codepageToEncoding(codepage, true);
- byte[] htmlBodyBinary = htmlBodyBinaryChunk.getValue();
- return new String(htmlBodyBinary, encoding);
- } catch (UnsupportedEncodingException e) {
- LOG.atWarn().log("HTML body binary: Invalid codepage ID {} set for the message via {}, ignoring", box(codepage), MAPIProperty.INTERNET_CPID);
- }
- }
- return htmlBodyBinaryChunk.getAs7bitString();
- }
- return getStringFromChunk(mainChunks.getHtmlBodyChunkString());
- }
-
- /**
- * Gets the RTF Rich Message body of this Outlook Message, if this email
- * contains a RTF (rich) version.
- * @return The string representation of the 'RTF' version of the body, if available.
- * @throws ChunkNotFoundException If the rtf-body chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getRtfBody() throws ChunkNotFoundException {
- ByteChunk chunk = mainChunks.getRtfBodyChunk();
- if(chunk == null) {
- if(returnNullOnMissingChunk) {
- return null;
- } else {
- throw new ChunkNotFoundException();
- }
- }
-
- try {
- MAPIRtfAttribute rtf = new MAPIRtfAttribute(
- MAPIProperty.RTF_COMPRESSED, Types.BINARY.getId(), chunk.getValue()
- );
- return rtf.getDataString();
- } catch(IOException e) {
- throw new RuntimeException("Shouldn't happen", e);
- }
- }
-
- /**
- * Gets the subject line of the Outlook Message
- * @throws ChunkNotFoundException If the subject-chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getSubject() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getSubjectChunk());
- }
-
- /**
- * Gets the display value of the "FROM" line of the outlook message
- * This is not the actual address that was sent from but the formatted display of the user name.
- * @throws ChunkNotFoundException If the from-chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getDisplayFrom() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getDisplayFromChunk());
- }
-
- /**
- * Gets the display value of the "TO" line of the outlook message.
- * If there are multiple recipients, they will be separated
- * by semicolons.
- * This is not the actual list of addresses/values that will be
- * sent to if you click Reply in the email - those are stored
- * in {@link RecipientChunks}.
- * @throws ChunkNotFoundException If the to-chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getDisplayTo() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getDisplayToChunk());
- }
-
- /**
- * Gets the display value of the "CC" line of the outlook message.
- * If there are multiple recipients, they will be separated
- * by semicolons.
- * This is not the actual list of addresses/values that will be
- * sent to if you click Reply in the email - those are stored
- * in {@link RecipientChunks}.
- * @throws ChunkNotFoundException If the cc-chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getDisplayCC() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getDisplayCCChunk());
- }
-
- /**
- * Gets the display value of the "BCC" line of the outlook message.
- * If there are multiple recipients, they will be separated
- * by semicolons.
- * This is not the actual list of addresses/values that will be
- * sent to if you click Reply in the email - those are stored
- * in {@link RecipientChunks}.
- * This will only be present in sent emails, not received ones!
- * @throws ChunkNotFoundException If the bcc-chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getDisplayBCC() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getDisplayBCCChunk());
- }
-
- /**
- * Returns all the recipients' email address, separated by
- * semicolons. Checks all the likely chunks in search of
- * the addresses.
- */
- public String getRecipientEmailAddress() throws ChunkNotFoundException {
- return toSemicolonList(getRecipientEmailAddressList());
- }
- /**
- * Returns an array of all the recipient's email address, normally
- * in TO then CC then BCC order.
- * Checks all the likely chunks in search of the addresses.
- */
- public String[] getRecipientEmailAddressList() throws ChunkNotFoundException {
- if(recipientChunks == null || recipientChunks.length == 0) {
- throw new ChunkNotFoundException("No recipients section present");
- }
-
- String[] emails = new String[recipientChunks.length];
- for(int i=0; i<emails.length; i++) {
- RecipientChunks rc = recipientChunks[i];
- String email = rc.getRecipientEmailAddress();
- if(email != null) {
- emails[i] = email;
- } else {
- if(returnNullOnMissingChunk) {
- emails[i] = null;
- } else {
- throw new ChunkNotFoundException("No email address holding chunks found for the " + (i+1) + "th recipient");
- }
- }
- }
-
- return emails;
- }
-
-
- /**
- * Returns all the recipients' names, separated by
- * semicolons. Checks all the likely chunks in search of
- * the names.
- * See also {@link #getDisplayTo()}, {@link #getDisplayCC()}
- * and {@link #getDisplayBCC()}.
- */
- public String getRecipientNames() throws ChunkNotFoundException {
- return toSemicolonList(getRecipientNamesList());
- }
- /**
- * Returns an array of all the recipient's names, normally
- * in TO then CC then BCC order.
- * Checks all the likely chunks in search of the names.
- * See also {@link #getDisplayTo()}, {@link #getDisplayCC()}
- * and {@link #getDisplayBCC()}.
- */
- public String[] getRecipientNamesList() throws ChunkNotFoundException {
- if(recipientChunks == null || recipientChunks.length == 0) {
- throw new ChunkNotFoundException("No recipients section present");
- }
-
- String[] names = new String[recipientChunks.length];
- for(int i=0; i<names.length; i++) {
- RecipientChunks rc = recipientChunks[i];
- String name = rc.getRecipientName();
- if(name != null) {
- names[i] = name;
- } else {
- throw new ChunkNotFoundException("No display name holding chunks found for the " + (i+1) + "th recipient");
- }
- }
-
- return names;
- }
-
- /**
- * Tries to identify the correct encoding for 7-bit (non-unicode)
- * strings in the file.
- * <p>Many messages store their strings as unicode, which is
- * nice and easy. Some use one-byte encodings for their
- * strings, but don't always store the encoding anywhere
- * helpful in the file.</p>
- * <p>This method checks for codepage properties, and failing that
- * looks at the headers for the message, and uses these to
- * guess the correct encoding for your file.</p>
- * <p>Bug #49441 has more on why this is needed</p>
- */
- public void guess7BitEncoding() {
- String generalcodepage = null;
- String htmlbodycodepage = null;
- String bodycodepage = null;
- //
- // General codepage: Message codepage property.
- //
- List<PropertyValue> val = mainChunks.getProperties().get(MAPIProperty.MESSAGE_CODEPAGE);
- if (val != null && val.size() > 0) {
- int codepage = ((LongPropertyValue) val.get(0)).getValue();
- try {
- String encoding = CodePageUtil.codepageToEncoding(codepage, true);
- generalcodepage = encoding;
- } catch (UnsupportedEncodingException e) {
- LOG.atWarn().log("Invalid codepage ID {} set for the message via {}, ignoring", box(codepage), MAPIProperty.MESSAGE_CODEPAGE);
- }
- }
- //
- // General codepage fallback: Message locale ID property.
- //
- if (generalcodepage == null) {
- val = mainChunks.getProperties().get(MAPIProperty.MESSAGE_LOCALE_ID);
- if (val != null && val.size() > 0) {
- int lcid = ((LongPropertyValue) val.get(0)).getValue();
- int codepage = LocaleUtil.getDefaultCodePageFromLCID(lcid);
- try {
- if (codepage != 0) {
- String encoding = CodePageUtil.codepageToEncoding(codepage, true);
- generalcodepage = encoding;
- }
- } catch (UnsupportedEncodingException e) {
- LOG.atWarn().log("Invalid codepage ID {}from locale ID{} set for the message via {}, ignoring", box(codepage),box(lcid), MAPIProperty.MESSAGE_LOCALE_ID);
- }
- }
- }
- //
- // General codepage fallback: Charset on a content type header.
- //
- if (generalcodepage == null) {
- try {
- String[] headers = getHeaders();
- if (headers != null && headers.length > 0) {
- Pattern p = Pattern.compile("content-type:.*?charset=[\"']?([^;'\"]+)[\"']?", Pattern.CASE_INSENSITIVE);
- for (String header : headers) {
- if (header.toLowerCase(LocaleUtil.getUserLocale()).startsWith("content-type")) {
- Matcher m = p.matcher(header);
- if (m.matches()) {
- String encoding = m.group(1);
- generalcodepage = encoding;
- }
- }
- }
- }
- } catch (ChunkNotFoundException e) {
- }
- }
- //
- // HTML and text body encoding: Internet CPID property.
- // UTF-8 is ignored for text body. This seems to be a special Outlook behavior.
- //
- val = mainChunks.getProperties().get(MAPIProperty.INTERNET_CPID);
- if (val != null && val.size() > 0) {
- int codepage = ((LongPropertyValue) val.get(0)).getValue();
- try {
- String encoding = CodePageUtil.codepageToEncoding(codepage, true);
- htmlbodycodepage = encoding;
- if (!encoding.equalsIgnoreCase("utf-8")) {
- bodycodepage = encoding;
- }
- } catch (UnsupportedEncodingException e) {
- LOG.atWarn().log("Invalid codepage ID {} set for the message via {}, ignoring", box(codepage), MAPIProperty.INTERNET_CPID);
- }
- }
- //
- // Apply encoding
- //
- set7BitEncoding(generalcodepage, htmlbodycodepage, bodycodepage);
- }
-
- /**
- * Many messages store their strings as unicode, which is
- * nice and easy. Some use one-byte encodings for their
- * strings, but don't easily store the encoding anywhere
- * in the file!
- * If you know what the encoding is of your file, you can
- * use this method to set the 7 bit encoding for all
- * the non unicode strings in the file.
- * @see #guess7BitEncoding()
- */
- public void set7BitEncoding(String charset) {
- set7BitEncoding(charset, charset, charset);
- }
- public void set7BitEncoding(String generalcharset, String htmlbodycharset, String bodycharset) {
- for(Chunk c : mainChunks.getChunks()) {
- if(c instanceof StringChunk) {
- if (c.getChunkId() == MAPIProperty.BODY_HTML.id) {
- if (htmlbodycharset != null) {
- ((StringChunk)c).set7BitEncoding(htmlbodycharset);
- }
- }
- else if (c.getChunkId() == MAPIProperty.BODY.id) {
- if (bodycharset != null) {
- ((StringChunk)c).set7BitEncoding(bodycharset);
- }
- }
- else if (generalcharset != null) {
- ((StringChunk)c).set7BitEncoding(generalcharset);
- }
- }
- }
- if (generalcharset != null) {
- if (nameIdChunks!=null) {
- for(Chunk c : nameIdChunks.getChunks()) {
- if(c instanceof StringChunk) {
- ((StringChunk)c).set7BitEncoding(generalcharset);
- }
- }
- }
- for(RecipientChunks rc : recipientChunks) {
- for(Chunk c : rc.getAll()) {
- if(c instanceof StringChunk) {
- ((StringChunk)c).set7BitEncoding(generalcharset);
- }
- }
- }
- }
- }
-
- /**
- * Does this file contain any strings that
- * are stored as 7 bit rather than unicode?
- */
- public boolean has7BitEncodingStrings() {
- for(Chunk c : mainChunks.getChunks()) {
- if(c instanceof StringChunk) {
- if( c.getType() == Types.ASCII_STRING ) {
- return true;
- }
- }
- }
-
- if (nameIdChunks!=null) {
- for(Chunk c : nameIdChunks.getChunks()) {
- if(c instanceof StringChunk) {
- if( c.getType() == Types.ASCII_STRING ) {
- return true;
- }
- }
- }
- }
-
- for(RecipientChunks rc : recipientChunks) {
- for(Chunk c : rc.getAll()) {
- if(c instanceof StringChunk) {
- if( c.getType() == Types.ASCII_STRING ) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Returns all the headers, one entry per line
- */
- public String[] getHeaders() throws ChunkNotFoundException {
- String headers = getStringFromChunk(mainChunks.getMessageHeaders());
- if(headers == null) {
- return null;
- }
- return headers.split("\\r?\\n");
- }
-
- /**
- * Gets the conversation topic of the parsed Outlook Message.
- * This is the part of the subject line that is after the RE: and FWD:
- * @throws ChunkNotFoundException If the conversation-topic chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public String getConversationTopic() throws ChunkNotFoundException {
- return getStringFromChunk(mainChunks.getConversationTopic());
- }
-
- /**
- * Gets the message class of the parsed Outlook Message.
- * (Yes, you can use this to determine if a message is a calendar
- * item, note, or actual outlook Message)
- * For emails the class will be IPM.Note
- *
- * @throws ChunkNotFoundException If the message-class chunk does not exist and
- * returnNullOnMissingChunk is set
- */
- public MESSAGE_CLASS getMessageClassEnum() throws ChunkNotFoundException {
- String mc = getStringFromChunk(mainChunks.getMessageClass());
- if (mc == null || mc.trim().length() == 0) {
- return MESSAGE_CLASS.UNSPECIFIED;
- } else if (mc.equalsIgnoreCase("IPM.Note")) {
- return MESSAGE_CLASS.NOTE;
- } else if (mc.equalsIgnoreCase("IPM.Contact")) {
- return MESSAGE_CLASS.CONTACT;
- } else if (mc.equalsIgnoreCase("IPM.Appointment")) {
- return MESSAGE_CLASS.APPOINTMENT;
- } else if (mc.equalsIgnoreCase("IPM.StickyNote")) {
- return MESSAGE_CLASS.STICKY_NOTE;
- } else if (mc.equalsIgnoreCase("IPM.Task")) {
- return MESSAGE_CLASS.TASK;
- } else if (mc.equalsIgnoreCase("IPM.Post")) {
- return MESSAGE_CLASS.POST;
- } else {
- LOG.atWarn().log("I don't recognize message class '{}'. Please open an issue on POI's bugzilla", mc);
- return MESSAGE_CLASS.UNKNOWN;
- }
- }
- /**
- * Gets the date that the message was accepted by the
- * server on.
- */
- public Calendar getMessageDate() throws ChunkNotFoundException {
- if (mainChunks.getSubmissionChunk() != null) {
- return mainChunks.getSubmissionChunk().getAcceptedAtTime();
- }
- else {
- // Try a few likely suspects...
- for (MAPIProperty prop : new MAPIProperty[] {
- MAPIProperty.CLIENT_SUBMIT_TIME, MAPIProperty.LAST_MODIFICATION_TIME,
- MAPIProperty.CREATION_TIME
- }) {
- List<PropertyValue> val = mainChunks.getProperties().get(prop);
- if (val != null && val.size() > 0) {
- return ((TimePropertyValue)val.get(0)).getValue();
- }
- }
- }
-
- if(returnNullOnMissingChunk)
- return null;
- throw new ChunkNotFoundException();
- }
-
-
- /**
- * Gets the main, core details chunks
- */
- public Chunks getMainChunks() {
- return mainChunks;
- }
- /**
- * Gets all the recipient details chunks.
- * These will normally be in the order of:
- * * TO recipients, in the order returned by {@link #getDisplayTo()}
- * * CC recipients, in the order returned by {@link #getDisplayCC()}
- * * BCC recipients, in the order returned by {@link #getDisplayBCC()}
- */
- public RecipientChunks[] getRecipientDetailsChunks() {
- return recipientChunks;
- }
- /**
- * Gets the Name ID chunks, or
- * null if there aren't any
- */
- public NameIdChunks getNameIdChunks() {
- return nameIdChunks;
- }
- /**
- * Gets the message attachments.
- */
- public AttachmentChunks[] getAttachmentFiles() {
- return attachmentChunks;
- }
-
-
- /**
- * Will you get a null on a missing chunk, or a
- * {@link ChunkNotFoundException} (default is the
- * exception).
- */
- public boolean isReturnNullOnMissingChunk() {
- return returnNullOnMissingChunk;
- }
-
- /**
- * Sets whether on asking for a missing chunk,
- * you get back null or a {@link ChunkNotFoundException}
- * (default is the exception).
- */
- public void setReturnNullOnMissingChunk(boolean returnNullOnMissingChunk) {
- this.returnNullOnMissingChunk = returnNullOnMissingChunk;
- }
-
-
- private String toSemicolonList(String[] l) {
- StringBuilder list = new StringBuilder();
- boolean first = true;
-
- for(String s : l) {
- if(s == null) continue;
- if(first) {
- first = false;
- } else {
- list.append("; ");
- }
- list.append(s);
- }
-
- return list.toString();
- }
- }
|