Some checks failed
Build & Push Multi-Arch Image / build-and-push (push) Has been cancelled
333 lines
15 KiB
Java
333 lines
15 KiB
Java
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 <classpath> Convert <input.ost> <output.pst/zip> [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();
|
|
}
|
|
}
|
|
}
|
|
}
|