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

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?

Retrieve available categories from PST:

using (var pst = PersonalStorage.FromFile("mailbox.pst"))
{
   var categories = pst.GetCategories();
   
   foreach(var category in categories)
   {
       Console.WriteLine(category);
   }
}

Matching a category name with its color:

using (var pst = PersonalStorage.FromFile("mailbox.pst"))
{
    // Get all categories from the PST
    var availableCategories = pst.GetCategories();
    
    // Extract a message from the PST and retrieve the list of category names for the message
    var messageCategoryList = FollowUpManager.GetCategories(pst.ExtractMessage(messageInfo));
    
    // Iterate through each category in the message and match it with the PST category list
    foreach (var messageCategory in messageCategoryList)
    {
        var category = availableCategories.Find(c => c.Name.Equals(messageCategory, StringComparison.OrdinalIgnoreCase));

        if (category != null)
        {
            // Print the category name and its associated color
            Console.WriteLine(category);
        }
        else
        {
            Console.WriteLine($"Category: {messageCategory}, Color: Not found");
        }
    }
}

And Java examples.

Retrieve available categories fronm PST:

try (PersonalStorage pst = PersonalStorage.fromFile("mailbox.pst")) {
    List<PstItemCategory> categories = pst.getCategories();

    for (PstItemCategory category : categories) {
        System.out.println(category);
    }
}

Matching a category name with its color:

try (PersonalStorage pst = PersonalStorage.fromFile("mailbox.pst")) {
    // Get all categories from the PST
    List<PstItemCategory> availableCategories = pst.getCategories();

    // Extract a message from the PST and retrieve the list of category names for the message
    Iterable<?> messageCategoryList = FollowUpManager.getCategories(pst.extractMessage(messageInfo));

    // Iterate through each category in the message and match it with the PST category list
    for (Object messageCategory : messageCategoryList) {
        PstItemCategory category = null;
        for (PstItemCategory c : availableCategories) {
            if (c.getName().equalsIgnoreCase(messageCategory.toString())) {
                category = c;
                break;
            }
        }

        if (category != null) {
            // Print the category name and its associated color
            System.out.println(category);
        } else {
            System.out.println("Category: " + messageCategory + ", Color: Not found");
        }
    }
}

Hii @margarita.samodurova the function which you shared pst.getCategories(); is always returning an empty array though the PST contains the categories inside it any idea how to resolve it?

I tried for the below pst file.

Outlook.pst.zip (656.0 KB)

Hello @MaazHussain,

The PST you provided doesn’t contain any categories. Additionally, there are no messages in this PST that have assigned categories.

Please note that you can retrieve the category-color mapping table only if the PST was created by Outlook and contains at least one item with an assigned category.

For PST files created using Aspose.Email, the category-color mapping table is not available, although you can still add items with assigned categories. This limitation exists due to the lack of a publicly available specification for implementing such a table.

EMAILJAVA-35352 ---- Status : Resolved

@sergey.vivsiuk / @margarita.samodurova The status is shown as resolved can you let us know in which version we can find it?

@MaazHussain,

This will be available in version 25.3, expected to be released early next month.

1 Like

@margarita.samodurova The requirement which we had was to fetch the limit of mails, was to extract the limit of messages from the folder and not the list of mails in that range.

So if say a folder has 500 emails and from 0 - 50 there are 20 unread emails, the fix now returns 20 emails.
But what we needed was when we provide 50 as the limit it has to return the first 50 unread emails instead of 20 which is getting returned now, is is possible to provide this fix?

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

@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-35364

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.

Thanks @sergey.vivsiuk .

Just to confirm again the case so we are on the same page.

The required fix is when i enter the start value as 0 and limit as 50 it should return the top 50 mails matching that condition. and if later i send the start value as 51 and then limit as 50 it should return from the 51st mail matching that condition till the limit.

Hello @MaazHussain ,

Thank you for the detailed explanation. We were able to reproduce the issue on a larger messages set. A ticket has been opened, and we’re actively working on a fix.

@sergey.vivsiuk

I also found the issue to resurface while trying to perform mark as read/ unread action on a PST file.

This is the code im trying. Can you please have this checked and let me know, im facing the same error for most of the pst files, it was working fine in the start but after a point of time I started getting this error and its constant from then on.


 markAsReadUnread(pst, "AAAAAHA09Qzhep5Mt2K+2wvBQ/rkACAA", true);


public static void markAsReadUnread(PersonalStorage pst, String messageId, boolean isUnread) {
        MapiMessage msg = pst.extractMessage(messageId);
        MapiPropertyCollection updatedProperties = new MapiPropertyCollection();

        long flags = msg.getFlags();
        if (isUnread) {
            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();
    }

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.<init>(SourceFile:85)
com.aspose.email.zahs.b(SourceFile:1004)
com.aspose.email.zavw.a(SourceFile:1261)
com.aspose.email.PersonalStorage.changeMessage(SourceFile:2125)
com.zoho.trident.ZPSTMailActions.markAsReadUnread(ZPSTMailActions.java:22)
com.zoho.trident.Main.main(Main.java:63)
	at com.aspose.email.system.collections.generic.Dictionary.addItem(Unknown Source)
	at com.aspose.email.zfs.a(SourceFile:540)
	at com.aspose.email.zfs.h(SourceFile:494)
	at com.aspose.email.zfs.<init>(SourceFile:85)
	at com.aspose.email.zahs.b(SourceFile:1004)
	at com.aspose.email.zavw.a(SourceFile:1261)
	at com.aspose.email.PersonalStorage.changeMessage(SourceFile:2125)
	at com.zoho.trident.ZPSTMailActions.markAsReadUnread(ZPSTMailActions.java:22)
	at com.zoho.trident.Main.main(Main.java:63)

Convo.pst.zip (88.8 KB)