import com.aspose.email.FileFormatVersion; import com.aspose.email.FolderInfo; import com.aspose.email.MapiMessage; import com.aspose.email.MessageInfo; import com.aspose.email.PersonalStorage; import com.aspose.email.MailMessage; import com.aspose.email.MailConversionOptions; import com.aspose.email.MboxrdStorageWriter; import com.aspose.email.MapiContact; import com.aspose.email.MapiItemType; import com.aspose.email.ContactSaveFormat; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class Convert { private static PersonalStorage sourceStorage; public static void main(String[] args) { if (args.length < 2) { System.err.println("Usage: java -cp Convert [format]"); System.exit(1); } String inputPath = args[0]; String outputPath = args[1]; String format = (args.length >= 3) ? args[2].toLowerCase() : "pst"; boolean combineVcf = (args.length >= 4) && args[3].equalsIgnoreCase("true"); System.out.println("Starting manual copy conversion of OST: " + inputPath + " to format: " + format.toUpperCase()); long startTime = System.currentTimeMillis(); try { // Delete existing output file if it exists File outFile = new File(outputPath); if (outFile.exists()) { System.out.println("Destination file already exists. Deleting " + outputPath + "..."); if (outFile.delete()) { System.out.println("Deleted existing file successfully."); } else { System.err.println("Warning: Failed to delete existing file."); } } // Load the source OST file System.out.println("Loading OST file..."); sourceStorage = PersonalStorage.fromFile(inputPath); if (format.equals("mbox")) { // Export folders to a temporary directory of .mbox files, then zip them File tempDir = new File("mbox_temp_" + System.currentTimeMillis()); if (!tempDir.exists()) { tempDir.mkdirs(); } System.out.println("Exporting folder structure, email messages, and contacts..."); exportFoldersToMbox(sourceStorage.getRootFolder(), "", tempDir, combineVcf); // If combineVcf was requested, merge all individual VCFs in the contacts subdirs if (combineVcf) { System.out.println("Merging contact VCF files into a single contacts.vcf..."); mergeVcfFiles(tempDir); } System.out.println("Packaging MBOX files and VCards into ZIP archive..."); zipDirectory(tempDir, outputPath); System.out.println("Cleaning up temporary MBOX/VCard files..."); cleanDirectory(tempDir); tempDir.delete(); long duration = System.currentTimeMillis() - startTime; System.out.println("Conversion completed successfully in " + (duration / 1000.0) + " seconds."); } else { // Default PST format System.out.println("Creating destination PST file..."); try (PersonalStorage destPst = PersonalStorage.create(outputPath, FileFormatVersion.Unicode)) { System.out.println("Copying folder structure and messages..."); copyFolders(sourceStorage.getRootFolder(), destPst.getRootFolder()); long duration = System.currentTimeMillis() - startTime; System.out.println("Conversion completed successfully in " + (duration / 1000.0) + " seconds."); } } sourceStorage.close(); System.exit(0); } catch (Exception e) { System.err.println("Error during conversion: " + e.getMessage()); e.printStackTrace(); if (sourceStorage != null) { try { sourceStorage.close(); } catch (Exception ex) { // Ignore } } System.exit(1); } } private static void copyFolders(FolderInfo sourceFolder, FolderInfo destParentFolder) { for (FolderInfo subFolder : sourceFolder.getSubFolders()) { String folderName = subFolder.getDisplayName(); System.out.println("Processing Folder: " + folderName); // Recreate folder in destination FolderInfo newDestFolder = destParentFolder.addSubFolder(folderName); // Copy messages int copiedCount = 0; for (MessageInfo msgInfo : subFolder.enumerateMessages()) { try { MapiMessage msg = sourceStorage.extractMessage(msgInfo); newDestFolder.addMessage(msg); copiedCount++; if (copiedCount % 100 == 0) { System.out.println(" Copied " + copiedCount + " messages..."); } } catch (Exception ex) { System.err.println(" Warning: Failed to copy message '" + msgInfo.getSubject() + "': " + ex.getMessage()); } } if (copiedCount > 0) { System.out.println(" Successfully copied " + copiedCount + " messages to folder: " + folderName); } // Recursively copy subfolders copyFolders(subFolder, newDestFolder); } } private static void exportFoldersToMbox(FolderInfo sourceFolder, String currentPath, File tempDir, boolean combineVcf) { for (FolderInfo subFolder : sourceFolder.getSubFolders()) { String folderName = subFolder.getDisplayName(); String fullPath = currentPath.isEmpty() ? folderName : currentPath + " - " + folderName; System.out.println("Processing Folder: " + folderName); boolean isContactsFolder = folderName.toLowerCase().contains("contacts"); // Skip other non-email folders if (folderName.toLowerCase().contains("calendrier") || folderName.toLowerCase().contains("journal") || folderName.toLowerCase().contains("tâches") || folderName.toLowerCase().contains("notes")) { System.out.println(" Skipping non-email/non-contact folder: " + folderName); // Still process children in case there are nested folders exportFoldersToMbox(subFolder, fullPath, tempDir, combineVcf); continue; } if (isContactsFolder) { System.out.println(" Processing contacts in folder: " + folderName); String safeSubDirName = "Contacts - " + folderName.replaceAll("[\\\\/:*?\"<>|]", "_"); File contactsDir = new File(tempDir, safeSubDirName); if (!contactsDir.exists()) { contactsDir.mkdirs(); } int contactCount = 0; for (MessageInfo msgInfo : subFolder.enumerateMessages()) { try { MapiMessage msg = sourceStorage.extractMessage(msgInfo); if (msg.getSupportedType() == MapiItemType.Contact) { MapiContact contact = (MapiContact) msg.toMapiMessageItem(); // Determine a suitable name for the .vcf file String displayName = null; if (contact.getNameInfo() != null) { displayName = contact.getNameInfo().getDisplayName(); } if (displayName == null || displayName.trim().isEmpty()) { if (contact.getElectronicAddresses() != null && contact.getElectronicAddresses().getEmail1() != null) { displayName = contact.getElectronicAddresses().getEmail1().getEmailAddress(); } } if (displayName == null || displayName.trim().isEmpty()) { displayName = "Contact_" + (contactCount + 1); } String safeContactName = displayName.replaceAll("[\\\\/:*?\"<>|]", "_") + ".vcf"; File vcfFile = new File(contactsDir, safeContactName); // Check for duplicates int index = 1; while (vcfFile.exists()) { String nameWithIndex = displayName.replaceAll("[\\\\/:*?\"<>|]", "_") + "_" + index + ".vcf"; vcfFile = new File(contactsDir, nameWithIndex); index++; } contact.save(vcfFile.getAbsolutePath(), ContactSaveFormat.VCard); contactCount++; } } catch (Exception ex) { System.err.println(" Warning: Failed to export contact: " + ex.getMessage()); } } if (contactCount > 0) { System.out.println(" Successfully exported " + contactCount + " contacts to folder: " + folderName); } else { contactsDir.delete(); } // Recursively walk subfolders exportFoldersToMbox(subFolder, fullPath, tempDir, combineVcf); continue; } // Otherwise, it's an email folder. Export to MBOX... int copiedCount = 0; String safeFileName = fullPath.replaceAll("[\\\\/:*?\"<>|]", "_") + ".mbox"; File mboxFile = new File(tempDir, safeFileName); MailConversionOptions options = new MailConversionOptions(); MboxrdStorageWriter writer = null; for (MessageInfo msgInfo : subFolder.enumerateMessages()) { try { if (writer == null) { writer = new MboxrdStorageWriter(mboxFile.getAbsolutePath(), false); } MapiMessage msg = sourceStorage.extractMessage(msgInfo); MailMessage mailMsg = msg.toMailMessage(options); writer.writeMessage(mailMsg); copiedCount++; if (copiedCount % 100 == 0) { System.out.println(" Exported " + copiedCount + " messages..."); } } catch (Exception ex) { System.err.println(" Warning: Failed to export message '" + msgInfo.getSubject() + "': " + ex.getMessage()); } } if (writer != null) { try { writer.close(); } catch (Exception ex) { // Ignore } } if (copiedCount > 0) { System.out.println(" Successfully copied " + copiedCount + " messages to folder: " + folderName); } else { // Delete empty mbox file if created if (mboxFile.exists()) { mboxFile.delete(); } } // Recursively walk subfolders exportFoldersToMbox(subFolder, fullPath, tempDir, combineVcf); } } /** * Walk all contact subdirectories, read each .vcf, and write them all * into a single contacts.vcf at the root of tempDir. * Then delete the now-merged individual files and empty subdirs. */ private static void mergeVcfFiles(File tempDir) throws Exception { File combinedVcf = new File(tempDir, "contacts.vcf"); try (FileOutputStream fos = new FileOutputStream(combinedVcf)) { for (File child : tempDir.listFiles()) { if (child.isDirectory() && child.getName().startsWith("Contacts")) { for (File vcf : child.listFiles()) { if (vcf.isFile() && vcf.getName().endsWith(".vcf")) { byte[] data = java.nio.file.Files.readAllBytes(vcf.toPath()); fos.write(data); // Ensure each vCard ends with a newline separator if (data.length > 0 && data[data.length - 1] != '\n') { fos.write('\n'); } } } // Remove the now-merged individual VCF directory cleanDirectory(child); child.delete(); } } } System.out.println(" Merged VCF written: " + combinedVcf.getName()); } private static void zipDirectory(File dir, String zipFilePath) throws Exception { try (FileOutputStream fos = new FileOutputStream(zipFilePath); ZipOutputStream zos = new ZipOutputStream(fos)) { zipFolder(dir, dir, zos); } } private static void zipFolder(File rootDir, File currentDir, ZipOutputStream zos) throws Exception { File[] files = currentDir.listFiles(); if (files != null) { byte[] buffer = new byte[8192]; for (File file : files) { if (file.isDirectory()) { zipFolder(rootDir, file, zos); } else { String relativePath = rootDir.toURI().relativize(file.toURI()).getPath(); try (FileInputStream fis = new FileInputStream(file)) { ZipEntry ze = new ZipEntry(relativePath); zos.putNextEntry(ze); int len; while ((len = fis.read(buffer)) > 0) { zos.write(buffer, 0, len); } zos.closeEntry(); } } } } } private static void cleanDirectory(File dir) { File[] files = dir.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { cleanDirectory(file); } file.delete(); } } } }