Incorrectly saving attachments for recurrent modified events in PST file

When i try save attachments to recurrent modified events and read it, i lost data for attach.
PST data file and unit test in uploaded file (report.7z).
aspose-email version 20.7

original attach_
ItemId: null
DisplayName: Dashboard.png
LongFileName: Dashboard.png
Extension: .png
Content(): com.aspose.email.zamt@6fff253c
BinaryData: 65921

_resaved attach
ItemId: null
DisplayName: Recurrence for modification (Mod 4)(Aspose.Email Evaluation) --> wrong name?
LongFileName: Recurrence for modification (Mod 4)(Aspose.Email Evaluation) --> wrong name?
Extension: null —> lost data
Content(): com.aspose.email.zamt@7eb01b12
BinaryData: null —> lost data

report.7z (77.6 KB)

Export Orig and export PST.png (44.6 KB)

@RustamGazizov,
Welcome to our community! Thank you for the query. Could you share a simple project reproducing the issue, please? But first, please check the issue with the latest version of Aspose.Email.

Hello. Reproducing in 21.4 version.
In first post i uploaded (report.7z) PST data file and code to reproduce this issue.

class Office365CalendarPSTExporterTest {

//todo place path to PST file from attach
private static String originPSTFile =;

@Test
public void AsposePSTAttachSaveTest() throws IOException {
	Locale defaultLocale = Locale.getDefault();
	try {
		Locale.setDefault(new Locale("en", "RU"));
		CurrentThreadSettings.setLocale("en-US");
	} finally {
		Locale.setDefault(defaultLocale);
	}

	MapiCalendar originMergedCalendar = prepareCalendar();
	MapiCalendar resavedCalendar = saveAndReadCelendar(originMergedCalendar);

	printCelendarAttach(resavedCalendar);

	originMergedCalendar.getAttachments().stream().forEach(a -> {
		Assertions.assertNotNull(a.getDisplayName());
		Assertions.assertNotNull(a.getLongFileName());
		Assertions.assertNotNull(a.getExtension());
		Assertions.assertNotNull(a.getContent());
		Assertions.assertNotNull(a.getBinaryData());
		Assertions.assertNotEquals(0, a.getBinaryData().length);
	});

	//will be aa.jpg
	resavedCalendar.getAttachments().stream().forEach(a -> {
		Assertions.assertNotNull(a.getDisplayName());
		Assertions.assertNotNull(a.getLongFileName());
		Assertions.assertNotNull(a.getExtension());
		Assertions.assertNotNull(a.getContent());
		Assertions.assertNotNull(a.getBinaryData());
		Assertions.assertNotEquals(0, a.getBinaryData().length);
	});

	//will be dashboard.png
	Assertions.assertNotEquals(0, resavedCalendar.getRecurrence().getRecurrencePattern().getExceptions().size());
	Assertions.assertNotNull(resavedCalendar.getRecurrence().getRecurrencePattern().getExceptions().get_Item(0).getAttachments());
	resavedCalendar.getRecurrence().getRecurrencePattern().getExceptions().get_Item(0).getAttachments().forEach(a -> {
		Assertions.assertNotNull(a.getDisplayName());
		Assertions.assertNotNull(a.getLongFileName());
		Assertions.assertNotNull(a.getExtension());
		Assertions.assertNotNull(a.getContent());
		Assertions.assertNotNull(a.getBinaryData());
		Assertions.assertNotEquals(0, a.getBinaryData().length);
	});
}

private static MapiCalendar saveAndReadCelendar(MapiCalendar originMergedCalendar) throws IOException {
	File tempFile = File.createTempFile("temp", ".pst");

	PersonalStorage pstOut = PersonalStorage.create(tempFile.getName(), FileFormatVersion.Unicode);
	FolderInfo forTest = pstOut.getRootFolder().addSubFolder("forTest");
	String calTestId = forTest.addMapiMessageItem(originMergedCalendar);
	pstOut.dispose();
	pstOut.close();

	PersonalStorage readIn = PersonalStorage.fromFile(tempFile.getName());
	MapiCalendar mapiCalendar = readCal(readIn, calTestId);
	readIn.close();
	tempFile.deleteOnExit();
	return mapiCalendar;
}

private static MapiCalendar prepareCalendar() {
	PersonalStorage originPST = PersonalStorage.fromFile(originPSTFile);
	FolderInfoCollection folderInfoCollection = originPST.getRootFolder().getSubFolders();
	MapiCalendar originMaster = readCal(originPST, "AAAAAK+k0s6Bxr5KtY2F7gcC91QkACAA");
	MapiCalendar originOccur = readCal(originPST, "AAAAAK+k0s6Bxr5KtY2F7gcC91REACAA");
	printCelendarAttach(originMaster);
	printCelendarAttach(originOccur);
	return exportModOccurrence(originOccur, originMaster, null);
}

private static MapiCalendar readCal(PersonalStorage storedPST, String id) {
	return (MapiCalendar) MapiMessage
		.fromMailMessage(
			storedPST
				.extractMessage(id)
				.toMailMessage(new MailConversionOptions()), new MapiConversionOptions(OutlookMessageFormat.Unicode))
		.toMapiMessageItem();
}

private static MapiCalendar exportModOccurrence(MapiCalendar occur, MapiCalendar master, Date originalDate) {
	Date originalStartDate = originalDate != null ? originalDate : occur.getStartDate();
	MapiCalendarRecurrencePattern pattern = master.getRecurrence().getRecurrencePattern();
	MapiCalendarExceptionInfo exceptionInfo = new MapiCalendarExceptionInfo();
	exceptionInfo.setOriginalStartDate(originalStartDate);
	exceptionInfo.setStartDateTime(occur.getStartDate());
	if (occur.isAllDay()) {
		exceptionInfo.setEndDateTime(occur.getStartDate());
	} else {
		exceptionInfo.setEndDateTime(occur.getEndDate());
	}
	exceptionInfo.setSubject(occur.getSubject());
	exceptionInfo.setLocation(occur.getLocation());
	exceptionInfo.setAttachments(occur.getAttachments());
	exceptionInfo.setBody(occur.getBody());

	pattern.getExceptions().addItem(exceptionInfo);
	pattern.getModifiedInstanceDates().addItem(occur.getStartDate());
	pattern.getDeletedInstanceDates().addItem(originalStartDate);
	return master;
}

private static void printCelendarAttach(MapiCalendar mapiCalendar) {
	System.out.println("______________");
	mapiCalendar.getAttachments().stream().forEach(a -> {
		System.out.println("ItemId: " + a.getItemId());
		System.out.println("DisplayName: " + a.getDisplayName());
		System.out.println("LongFileName: " + a.getLongFileName());
		System.out.println("Extension: " + a.getExtension());
		System.out.println("Content(): " + a.getContent());
		System.out.println("BinaryData: " + (a.getBinaryData() == null ? null : a.getBinaryData().length));
	});
}

}

@RustamGazizov,
I logged the issue in our tracking system with ID EMAILJAVA-34882. Our development team will investigate this case. I will inform you about any progress.

@RustamGazizov,
Our development team is investigating this case. Is a double conversion necessary in the readCal method (MapiMessage.toMailMessage.toMapiMessage.toMapiMessageItem)? Or can we do it without additional conversion (MapiMessage.toMapiMessageItem)?

private static MapiCalendar readCal(PersonalStorage storedPST, String id) {
    return (MapiCalendar) storedPST.extractMessage(id).toMapiMessageItem();
}

Hello. Thanks for working on my issue. Yes, this is part of the complex business logic.

@RustamGazizov,
Thanks, our development team is continuing the investigation.

1 Like