Working with Zimbra
About Zimbra
Zimbra is an email, calendar and collaboration suite built for the cloud. Zimbra includes complete email, contacts, calendar, file sharing, tasks and messaging/videoconferencing, all accessed from the Zimbra Web Client from any device.
TGZ is the archive format that Zimbra Collaboration Suite uses to export and back up mailbox data. A .tgz file is a tar archive compressed with gzip: the tar part preserves the mailbox folder structure, and the gzip part compresses it so the backup is small and easy to move between systems. Inside the archive, each mail item is stored as an individual message together with the path of the folder it belongs to.
Aspose.Email for Java reads a Zimbra TGZ backup with the TgzReader class. The reader streams through the archive one item at a time, so it can process very large mailboxes without loading the whole backup into memory. It exposes the current folder and message through the getCurrentDirectory() and getCurrentMessage() methods.
Open a TGZ File
A TGZ file can be opened by passing the full file name to the TgzReader constructor. Because TgzReader implements Closeable, wrap it in a try-with-resources statement so that the underlying file handle is released when you are done.
String fileName = "mailbox.tgz";
try (TgzReader reader = new TgzReader(fileName)) {
// work with the backup
}
Open a TGZ File from a Stream
When the TGZ data does not come from a local path — for example, it is received from a database, a network resource, or cloud storage — you can open it from a stream. Wrap the Java InputStream with Stream.fromJava and pass the result to the constructor:
try (InputStream stream = Files.newInputStream(Paths.get("mailbox.tgz"));
TgzReader reader = new TgzReader(com.aspose.email.system.io.Stream.fromJava(stream))) {
// work with the backup
}
Read all messages from Zimbra TGZ storage
Aspose.Email provides the TgzReader class to read Zimbra TGZ storage files. The following sample code demonstrates the use of the TgzReader class to read all messages from the file.
Iterate Over the Items
When a TgzReader is created, it is positioned at the first item in the archive. The getCurrentDirectory() method returns the folder of the current item, and getCurrentMessage() returns the current message as a MailMessage. The readNextMessage() method advances the reader to the next item and returns true while there are more items to read, and false once the end of the archive is reached.
Because the reader already points at the first item, use a do...while loop so that the first item is processed before the reader is advanced:
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
do {
System.out.println("Folder: " + reader.getCurrentDirectory());
MailMessage message = reader.getCurrentMessage();
if (message != null) {
System.out.println("Subject: " + message.getSubject());
}
} while (reader.readNextMessage());
}
Read Message Properties
getCurrentMessage() returns a full MailMessage object, so all of the standard message properties — subject, sender, recipients, date, body, and attachments — are available while you iterate.
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
do {
MailMessage message = reader.getCurrentMessage();
if (message == null) {
continue;
}
System.out.println("Folder: " + reader.getCurrentDirectory());
System.out.println("Subject: " + message.getSubject());
System.out.println("From: " + message.getFrom());
System.out.println("To: " + message.getTo());
System.out.println("Date: " + message.getDate());
System.out.println("Attachments: " + message.getAttachments().size());
System.out.println("------------------------------");
} while (reader.readNextMessage());
}
Count the Items in a Backup
To find out how many message items a backup contains — for example, to show progress while processing — call the getTotalItemsCount() method.
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
int totalItems = reader.getTotalItemsCount();
System.out.println("The backup contains " + totalItems + " items.");
}
You can combine the count with the iteration to report progress:
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
int total = reader.getTotalItemsCount();
int processed = 0;
do {
processed++;
MailMessage message = reader.getCurrentMessage();
System.out.println("Processing item " + processed + " of " + total + ": "
+ (message == null ? "" : message.getSubject()));
} while (reader.readNextMessage());
}
Save Messages and Directory Structure
You may also save all the message with directory structure from the Zimbra TGZ storage file. For this, the TgzReader class provides a method ExportTo which takes the output path as a parameter.
The following code snippet demonstrates the use of the TgzReader.ExportTo method to save all messages from the Zimbra TGZ storage file.
Export calendar and contact items from Zimbra backup files
Aspose.Email makes it possible to export Zimbra’s calendar and contacts to iCalendar and VCard formats. The code sample below shows how to implement this feature into our project:
try (TgzReader reader = new TgzReader("test2.tgz")) {
//contacts files can be found in Contacts and Emailed Contacts subfolders
//calendar files can be found in Calendar subfolder
reader.exportTo("out");
}
Save Selected Messages
When you only need some of the messages — for example, the contents of a single folder, or messages that match a condition — iterate over the backup and save the current message yourself. Because it is a MailMessage, you can save it in any supported format, such as EML or MSG.
The example below saves only the messages from the Inbox folder in EML format:
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
int index = 0;
do {
MailMessage message = reader.getCurrentMessage();
if (message == null) {
continue;
}
if (reader.getCurrentDirectory().toLowerCase().startsWith("inbox")) {
message.save("Export/Inbox/message_" + index++ + ".eml", SaveOptions.getDefaultEml());
}
} while (reader.readNextMessage());
}
To save messages in Outlook MSG format instead, use the corresponding SaveOptions:
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
int index = 0;
do {
MailMessage message = reader.getCurrentMessage();
if (message != null) {
message.save("Export/Inbox/message_" + index++ + ".msg", SaveOptions.getDefaultMsgUnicode());
}
} while (reader.readNextMessage());
}
Filter Messages While Extracting
Since each item is exposed as a full MailMessage, you can apply any condition on its properties before saving. The following example extracts only the messages that have attachments:
try (TgzReader reader = new TgzReader("mailbox.tgz")) {
int index = 0;
do {
MailMessage message = reader.getCurrentMessage();
if (message != null && message.getAttachments().size() > 0) {
message.save("Export/WithAttachments/message_" + index++ + ".eml", SaveOptions.getDefaultEml());
}
} while (reader.readNextMessage());
}
Converting Zimbra TGZ to PST
A common migration scenario is moving a mailbox from Zimbra to Microsoft Outlook. Because a Zimbra TGZ backup stores each message together with the path of its folder, you can rebuild the same folder hierarchy inside an Outlook PST file and add every message to the matching folder. Aspose.Email for Java lets you do this in code, without Zimbra or Outlook installed.
How the Conversion Works
- Open the source backup with the TgzReader class.
- Create a new PST file with the PersonalStorage.create method.
- Iterate over the items in the backup. For each item, use
getCurrentDirectory()to find or create the corresponding folder in the PST, and addgetCurrentMessage()to it. - Convert each MailMessage to a MapiMessage with
MapiMessage.fromMailMessagebefore adding it, because PST folders store messages in the MAPI format.
Code Sample
The following code shows how to convert a Zimbra TGZ backup to PST while preserving its folder structure.
// Open the source TGZ backup and create a new PST file
try (TgzReader reader = new TgzReader("mailbox.tgz");
PersonalStorage pst = PersonalStorage.create("mailbox.pst", FileFormatVersion.Unicode)) {
do {
MailMessage message = reader.getCurrentMessage();
if (message == null) {
continue;
}
// Find or create the folder that matches the message directory
FolderInfo folder = getOrCreateFolder(pst, reader.getCurrentDirectory());
// PST folders store messages in the MAPI format
folder.addMessage(MapiMessage.fromMailMessage(message));
} while (reader.readNextMessage());
}
The folder path reported by getCurrentDirectory() uses the forward slash (/) as a separator, for example Inbox/Projects/2023. The helper below walks that path segment by segment, creating each missing sub-folder so that the PST mirrors the original Zimbra hierarchy.
public static FolderInfo getOrCreateFolder(PersonalStorage pst, String directory) {
FolderInfo folder = pst.getRootFolder();
if (directory == null || directory.isEmpty()) {
return folder;
}
for (String name : directory.split("/")) {
if (name.isEmpty()) {
continue;
}
FolderInfo subFolder = folder.getSubFolder(name);
folder = (subFolder != null) ? subFolder : folder.addSubFolder(name);
}
return folder;
}
MapiMessage.fromMailMessage preserves the standard message fields, body, and attachments. Server-specific Zimbra metadata that has no MAPI equivalent is not carried over.
ExportToAsync) available in Aspose.Email for .NET has no counterpart in Aspose.Email for Java — run exportTo on a worker thread if you need to keep the calling thread free.