How to get the following values and what they mean in PST File via Java

Hi team, I would like to know how to parse the below details in Java and what the numbers mean when i get the same for the below:

  1. Flags
  2. Categories
  3. Priority
  4. Mail Read Status and Unread status

Below is the code which i used, in which getFlags gives output as 1, 0 and 17 which i dont know what it means, and rest Categories, Priorities and Read/ Unread status im unable to fetch, can you look into it and help me out

Note: Im using the Licensed Paid Version.

FolderInfo folder = pst.getFolderById(folderId);
        MessageInfoCollection messages = folder.getContents(start, limit);
        int totalMessages = folder.getContentCount();
        int end = Math.min(start + limit, totalMessages);
        JSONArray mailsArray = new JSONArray();

        // Iterate only over the messages in the requested range.
        for (int i = 0; i < messages.size(); i++) {
            MessageInfo messageInfo = messages.get_Item(i);
            JSONObject msgJson = new JSONObject();
            msgJson.put("messageId", messageInfo.getEntryIdString());
            msgJson.put("subject", messageInfo.getSubject());
            msgJson.put("displayTo", messageInfo.getDisplayTo());
            msgJson.put("displayCC", messageInfo.getDisplayCC());
            msgJson.put("Importance", messageInfo.getImportance());


            // Extract the full message.
            MapiMessage msg = pst.extractMessage(messageInfo.getEntryIdString());
//            msgJson.put("textBody", msg.getBody());
            msgJson.put("fromAddress", msg.getSenderEmailAddress());
            msgJson.put("senderName", msg.getSenderName());
            msgJson.put("deliveryTime", msg.getDeliveryTime().getTime());
            msgJson.put("sentTime", msg.getClientSubmitTime().getTime());
            msgJson.put("conversationTopic", msg.getConversationTopic());
            msgJson.put("GetFlags", msg.getFlags());
            msgJson.put("hasAttachments", !msg.getAttachments().isEmpty());

            // Recipients details.
            JSONArray recipientsArray = new JSONArray();
            for (MapiRecipient recipient : msg.getRecipients()) {
                JSONObject recJson = new JSONObject();
                recJson.put("displayName", recipient.getDisplayName());
                recJson.put("emailAddress", recipient.getEmailAddress());
                recJson.put("addressType", recipient.getAddressType());
                recJson.put("recipientType", recipient.getRecipientType());
                recipientsArray.put(recJson);
            }
            msgJson.put("recipients", recipientsArray);

            // Optionally, you can add attachment details.
            JSONArray attachmentsArray = new JSONArray();
            for (MapiAttachment attachment : msg.getAttachments()) {
                JSONObject attJson = new JSONObject();
                attJson.put("displayName", attachment.getDisplayName());
                attachmentsArray.put(attJson);
            }
            msgJson.put("attachments", attachmentsArray);

            mailsArray.put(msgJson);
        }```

Hello @MaazHussain,

Thanks for reaching out. We’re looking into your request and will provide the necessary details shortly.

@MaazHussain,

Here’s how you can parse and interpret the requested properties:

  1. Flags
    The getFlags() method returns a combination of bitwise flags that represent different properties of the email. Common values include:
  • 0 → No flags set
  • 1 → Read
  • 2 → Unsent
  • 4 → Submitted
  • 8 → Unsendable
  • 16 (0x10) → Has attachments
  • 17 (0x11) → Read + Has attachments

To check for specific flags, use bitwise operations:

int flags = msg.getFlags();
boolean isRead = (flags & 1) != 0;
boolean hasAttachments = (flags & 16) != 0;
  1. Categories
    You can retrieve them using getCategories method:
String[] categories = msg.getCategories();
  1. Priority
    Message priority can be obtained from:
int priority = msg.getPropertyLong(MapiPropertyTag.PR_IMPORTANCE); 
// 0 = Low, 1 = Normal, 2 = High
  1. Mail Read/Unread Status
    The read/unread status can be determined using:
boolean isRead = (msg.getFlags() & MapiMessageFlags.MSGFLAG_READ) != 0;

Thanks @margarita.samodurova the above ones worked.

Now I am facing issues in setting the values to the PST file. My usecase is to edit the changes i make directly in the PST file, any idea how i can do it?

I used the below code to mark the emails as unread, add few categories to them, and to set the priority to High, then add a follow up flag.

But when checking it has not changed in the pst file any idea how to do it?

PersonalStorage pst = PersonalStorage.fromFile("/Users/MyAccount/Documents/large.pst", true);
String jsonResponse = ZPSTMailListParsing.setMessages(pst, "AAAAAMeWI+N6XchHqYzVFOtHCSCCgAAA");

public static String setMessages(PersonalStorage pst, String folderId) throws IOException {
        FolderInfo folder = pst.getFolderById(folderId);
        MessageInfoCollection messages = folder.getContents();
        for (int i = 0; i < messages.size(); i++) {
            MessageInfo messageInfo = messages.get_Item(i);
            MapiMessage msg = pst.extractMessage(messageInfo.getEntryIdString());
            String[] temp = {"XYZ Category", "ABC"};
            msg.setCategories(temp);
            msg.setMessageFlags(msg.getFlags() & ~MapiMessageFlags.MSGFLAG_READ);
            msg.setProperty(new MapiProperty(MapiPropertyTag.PR_IMPORTANCE, BitConverter.getBytes((byte) 2)));
            msg.setProperty(new MapiProperty(MapiPropertyTag.PR_FLAG_STATUS, BitConverter.getBytes((byte) 1)));
        }
    pst.dispose()
}


@MaazHussain,

The extracted message is independent of the PST file, meaning your changes won’t be reflected in it.
Let us take some time to discuss possible ways to edit PST files with your case.

Any update on the above request @margarita.samodurova

Hello @MaazHussain ,

You can use the PersonalStorage.changeMessage() method as shown in the code example below:

// Load the PST file
PersonalStorage pst = PersonalStorage.fromFile("t.pst");

// Get the "Inbox" folder
FolderInfo inbox = pst.getRootFolder().getSubFolder("Inbox");

// List to store message entry IDs
java.util.List<String> ids = new ArrayList<>();

// Iterate through all message entry IDs in the "Inbox" folder
for (String entryId : inbox.enumerateMessagesEntryId()) {
    ids.add(entryId);
}

// Process each message by its entry ID
for (String id : ids) {
    // Extract the message from the PST file
    MapiMessage msg = pst.extractMessage(id);
    
    // Create a collection of updated properties for the message
    MapiPropertyCollection updatedProperties = new MapiPropertyCollection();
    
    // Get the current message flags
    long flags = msg.getFlags();
    
    // Clear the MSGFLAG_READ to mark the message as unread
    flags &= ~MapiMessageFlags.MSGFLAG_READ;
    updatedProperties.add(new MapiProperty(MapiPropertyTag.PR_MESSAGE_FLAGS, intToPT_LONG((int) flags)));
    
    // Set high importance for the message (2 - High Importance)
    updatedProperties.add(new MapiProperty(MapiPropertyTag.PR_IMPORTANCE, intToPT_LONG(2)));
    
    // Add a category to the message
    MapiNamedProperty categoryProperty = new MapiNamedProperty(
            "Keywords",
            KnownPropertySets.PUBLIC_STRINGS,
            new MapiProperty(0x8000101F, stringsToPT_MV_UNICODE(new String[]{"XYZ Category", "ABC"}))
    );
    updatedProperties.add(0x8000101F, categoryProperty);
    
    // Apply the updated properties to the message
    pst.changeMessage(id, updatedProperties);
}

// Dispose of the PST object
pst.dispose();
public static byte[] intToPT_LONG(int value) {
    return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array();
}

public static byte[] stringsToPT_MV_UNICODE(String[] values) throws IOException {
    ByteArrayOutputStream result = new ByteArrayOutputStream();
    ByteArrayOutputStream data = new ByteArrayOutputStream();

    result.write(intToPT_LONG(values.length));

    int currentOffset = 4 + values.length * 4;
    for (String value : values) {
        result.write(intToPT_LONG(currentOffset));
        byte[] encodedValue = value.getBytes(StandardCharsets.UTF_16LE);
        currentOffset += encodedValue.length;
        data.write(encodedValue);
    }

    result.write(data.toByteArray());
    return result.toByteArray();
}

Thanks @sergey.vivsiuk, I did try the above code you gave but im facing this error all the time when i try to run it? The same happens when i try for different actions like Flag, Category and Priority.

public class ZPSTMailActions {
    public static void markAsReadUnread(PersonalStorage pst, String messageId, boolean isRead) throws IOException {
        MapiMessage msg = pst.extractMessage(messageId);
        MapiPropertyCollection updatedProperties = new MapiPropertyCollection();

        long flags = msg.getFlags();
        if (isRead) {
            flags |= MapiMessageFlags.MSGFLAG_READ;
        } else {
            flags &= ~MapiMessageFlags.MSGFLAG_READ;
        }

        updatedProperties.add(new MapiProperty(MapiPropertyTag.PR_MESSAGE_FLAGS, intToPT_LONG((int) flags)));

        pst.changeMessage(messageId, updatedProperties);
        pst.dispose();
    }
}

Im calling this from the Main function in this manner

public static void main(String[] args) throws IOException {
        PersonalStorage pst = PersonalStorage.fromFile("/Users/maaz-17085/Downloads/test.pst");
        String messageId = "AAAAAMeWI+N6XchHqYzVFOtHCSAkACAA";
        ZPSTMailActions.markAsReadUnread(pst, messageId, true);
}

Error:

Exception in thread “main” class com.aspose.email.system.exceptions.ArgumentException: An element with the same key already exists in the dictionary.
com.aspose.email.system.collections.generic.Dictionary.addItem(Unknown Source)
com.aspose.email.zfs.a(SourceFile:540)
com.aspose.email.zfs.h(SourceFile:494)
com.aspose.email.zfs.(SourceFile:85)
com.aspose.email.zaho.b(SourceFile:781)
com.aspose.email.zavv.a(SourceFile:1028)
com.aspose.email.PersonalStorage.changeMessage(SourceFile:1502)
com.zoho.trident.ZPSTMailActions.markAsReadUnread(ZPSTMailActions.java:27)
com.zoho.trident.Main.main(Main.java:39)

Hello @MaazHussain ,

We have tested the code we shared with you. Reassigning properties does not cause errors with the latest version of Aspose.Email for Java.

It is likely that you are using an older version of Aspose.Email for Java 21.1.
Could you check the functionality with the latest version, Aspose.Email for Java 25.1?

Thanks @sergey.vivsiuk I think the pst file i was accessing was corrupt, it worked when i tried with a new file.

I have one more query, i need to know if the attachment i have is an Attachment or an Inline Image. Is there any key to identify the same?
Also i want to know how i can update the image to the html body, can you provide with a snippet for the same?

<img width=640 height=168 id="_x0000_i1025" src="cid:image001.png@01CC3097.BF0BAC70" alt="http://www.aspose.com/Newsletters/062011/images/aspose-newsletter-header-june-2011.png\">

Now when i fetch the attachments using this i get this

for (MapiAttachment attachment : message.getAttachments()) {
    String fileName = attachment.getLongFileName(); // image001.png
}

Now how can i identify if this is an inline image, should we just check the suffix of cid: and match with attachments name? or is there any other alternative?

Hello @MaazHussain ,

You can refer to the documentation where you can find a code example for determining the attachment type:

Also, in version 22.12, the MapiAttachment.IsInline property was added:

1 Like

Hii @sergey.vivsiuk I have another query.

The below code used PersonalStorageQueryBuilder to filter the required params. So the below code works fine for fetching these

  1. Has Attachments
  2. Is Unread
  3. Importance

I want to know if I can fetch the categories with specific names? And also want to know if i can fetch mails that are flagged for follow up

  1. Search with Categories
  2. Search with Flag - No Flag, Follow Up, Completed

Also do let me know if this will work only on folder specific and how to make it work for whole PST mail box ?

 public static String getFilteredMessages(PersonalStorage pst, String folderId, boolean hasAttachments, Boolean isUnread, int importance) throws IOException {
        FolderInfo folder = pst.getFolderById(folderId);
        PersonalStorageQueryBuilder queryBuilder = new PersonalStorageQueryBuilder();
        if (hasAttachments) {
            queryBuilder.hasFlags(MapiMessageFlags.MSGFLAG_HASATTACH);
        }

        if (isUnread != null) {
            queryBuilder.hasNoFlags(MapiMessageFlags.MSGFLAG_READ);

        }

        if(importance == 2) {
            queryBuilder.getImportance().equals((int) MapiImportance.High);
        } else if(importance == 1) {
            queryBuilder.getImportance().equals((int) MapiImportance.Normal);
        } else if(importance == 0) {
            queryBuilder.getImportance().equals((int) MapiImportance.Low);
        }
        
        // Retrieve messages using the built query.
        MessageInfoCollection messages = folder.getContents(queryBuilder.getQuery());
        System.out.println(messages)
}

Hello @MaazHussain ,

Filtering data by categories and status flag is not possible.
You need to fully load the message to access these properties.

MessageInfoCollection messages = folder.getContents(queryBuilder.getQuery());
for (MessageInfo messageInfo : messages)
{
    MapiMessage msg = pst.extractMessage(messageInfo);
    
    // using FollowUpManager
    FollowUpOptions options = FollowUpManager.getOptions(msg);
    if (options.getCategories().contains("Red")) //all categories joined by ;
        System.out.println("Category Red");
    if (options.isCompleted())
        System.out.println("Completed");

    // get the flag status property
    Long status = msg.getPropertyLong(MapiPropertyTag.PR_FLAG_STATUS);
    if (status == null || status == 0)
        System.out.println("Unflagged");
    else if (status == 1)
        System.out.println("Flagged complete");
    else if (status == 2)
        System.out.println("Flagged");

}

Thanks @sergey.vivsiuk

One more doubt, is there a way I can group a conversation and show them?

I did try the below code to get the conversationId/ threadId, but can you let me know how we can group it now.

private static String getThreadId(MapiMessage msg, MessageInfo messageInfo) {
        // Attempt to get Conversation Index property (Tag: 0x0071)
        MapiProperty convIndexProp = msg.getProperties().get_Item(MapiPropertyTag.PR_CONVERSATION_INDEX);
        if (convIndexProp != null && convIndexProp.getData() != null && convIndexProp.getData().length >= 22) {
            byte[] headerBytes = Arrays.copyOfRange(convIndexProp.getData(), 0, 22);
            return bytesToHex(headerBytes);
        }
        // Fallback to EntryId if ConversationIndex is missing
        return messageInfo.getEntryIdString();
    }

So, the above code gives me a conversationId of a mail, and the same id is present in say 10 other mails in same folder or in different folders. Now is there a function through which i can fetch all of those 10 mails with just the conversationId instead of iterating through each mail in each folder?

Hello @MaazHussain ,

You can use the method PersonalStorageQueryBuilder.findConversationThread(MessageInfo):

if (messageInfo.getProperties().get_Item(MapiPropertyTag.PR_CONVERSATION_INDEX) != null) {
    List<MessageInfo> result = new List<>();
    findConversationThread(messageInfo, pst.getRootFolder(), result);
    System.out.println(messageInfo.getSubject() + " " + result.size());
    System.out.println("------------------------------------------------");
    for (MessageInfo message : result) {
        System.out.println("from " + message.getSenderRepresentativeName() + " to " + message.getDisplayTo());
    }
    System.out.println("================================================");
}

private void findConversationThread(MessageInfo messageInfo, FolderInfo folder, List<MessageInfo> result) {
    PersonalStorageQueryBuilder queryBuilder = new PersonalStorageQueryBuilder();
    queryBuilder.findConversationThread(messageInfo);
    result.addAll(folder.getContents(queryBuilder.getQuery()));
    for (FolderInfo subFolder : folder.getSubFolders()) {
        findConversationThread(messageInfo, subFolder, result);
    }
}

Got it… @sergey.vivsiuk

Is there any way i can limit or pass the start and limit while fetching using a PersonalStorageQueryBuilder something like this?

folder.getContents(queryBuilder.getQuery()), start, limit);

Cause it fetches all the mails matching the query and this causes delay if folder has lot of mails. Do we have Pagination support for getting contents via query?

@MaazHussain
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): EMAILJAVA-35352

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

@sergey.vivsiuk Thanks, how will we be able to track the status of that internal ticket?

Can you also tell if there is a way to find the color of any category?

MapiMessage msg = pst.extractMessage(messageInfo.getEntryIdString());
String[] categories = msg.getCategories(); // Returns String array with category names

Hello @MaazHussain,

You can check the status on the Issues Status panel at the bottom of this page.

The MapiMessage.getCategories() method returns only category names, but it does not provide color information. The mapping between categories and their colors is stored within PST files. Therefore, you can determine the color only if you know exactly which PST file the MSG was extracted from and have access to it.

Hi @margarita.samodurova, I do know which PST file I am trying to access can you give the code snippet to find the color of the category?