use of com.zimbra.cs.service.util.ItemData in project zm-mailbox by Zimbra.
the class ArchiveFormatterTest method tagDecode.
@Test
public void tagDecode() throws Exception {
Account acct = Provisioning.getInstance().get(AccountBy.name, "test@zimbra.com");
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(acct);
ItemData id = new ItemData(mbox.getFolderById(null, Mailbox.ID_FOLDER_INBOX));
id.tags = null;
String[] tags = ArchiveFormatter.getTagNames(id);
Assert.assertNotNull(tags);
Assert.assertEquals("null -> no tags", 0, tags.length);
tags = new String[] { "foo" };
id.tags = ItemData.getTagString(tags);
Assert.assertEquals("single tag encoding", "foo", id.tags);
Assert.assertArrayEquals("single tag", tags, ArchiveFormatter.getTagNames(id));
tags = new String[] { "fo:o" };
id.tags = ItemData.getTagString(tags);
Assert.assertEquals("single tag encoding w/colon", "fo\\:o", id.tags);
Assert.assertArrayEquals("single tag w/colon", tags, ArchiveFormatter.getTagNames(id));
tags = new String[] { "foo", "bar" };
id.tags = ItemData.getTagString(tags);
Assert.assertEquals("two tag encoding", "foo:bar", id.tags);
Assert.assertArrayEquals("two tags", tags, ArchiveFormatter.getTagNames(id));
tags = new String[] { "fo:o", "ba\\r" };
id.tags = ItemData.getTagString(tags);
Assert.assertEquals("two tag encoding w/colon, backslash", "fo\\:o:ba\\\\r", id.tags);
Assert.assertArrayEquals("two tags w/colon, backslash", tags, ArchiveFormatter.getTagNames(id));
tags = new String[] { "1-Tag", "2-Tag" };
id.tags = ItemData.getTagString(tags);
Assert.assertEquals("Tags starting with numerics", "1-Tag:2-Tag", id.tags);
Assert.assertArrayEquals("Tags starting with numerics", tags, ArchiveFormatter.getTagNames(id));
}
use of com.zimbra.cs.service.util.ItemData in project zm-mailbox by Zimbra.
the class ItemDataFile method addFile.
static void addFile(File f, String topdir, Set<MailItem.Type> types, TarOutputStream tos) throws IOException {
ItemData id = null;
String path = f.getPath();
File mf = new File(path + ".meta");
TarEntry te;
MailItem.Type type;
if (path.indexOf(topdir) == 0)
path = path.substring(topdir.length() + 1);
path = path.replace('\\', '/');
if (mf.exists()) {
byte[] meta = new byte[(int) mf.length()];
FileInputStream fis = new FileInputStream(mf);
if (fis.read(meta) != mf.length()) {
throw new IOException("meta read err: " + f.getPath());
}
fis.close();
id = new ItemData(meta);
type = MailItem.Type.of(id.ud.type);
if (skip(types, type)) {
return;
}
te = new TarEntry(path + ".meta");
System.out.println(te.getName());
te.setGroupName(MailItem.Type.of(id.ud.type).toString());
te.setMajorDeviceId(id.ud.type);
te.setModTime(mf.lastModified());
te.setSize(meta.length);
tos.putNextEntry(te);
tos.write(meta);
tos.closeEntry();
} else {
if (path.endsWith(".csv") || path.endsWith(".vcf")) {
type = MailItem.Type.CONTACT;
} else if (path.endsWith(".eml")) {
type = MailItem.Type.MESSAGE;
} else if (path.endsWith(".ics")) {
if (path.startsWith("Tasks/")) {
type = MailItem.Type.TASK;
} else {
type = MailItem.Type.APPOINTMENT;
}
} else if (path.endsWith(".wiki")) {
type = MailItem.Type.WIKI;
} else {
type = MailItem.Type.DOCUMENT;
}
if (skip(types, type)) {
}
return;
}
if (f.exists() && !f.isDirectory() && (id != null || types == null)) {
byte[] buf = new byte[TarBuffer.DEFAULT_BLKSIZE];
FileInputStream fis = new FileInputStream(f);
int in;
te = new TarEntry(path);
System.out.println(te.getName());
te.setGroupName(MailItem.Type.of(id.ud.type).toString());
te.setMajorDeviceId(id.ud.type);
te.setModTime(mf.lastModified());
te.setSize(f.length());
tos.putNextEntry(te);
while ((in = fis.read(buf)) > 0) tos.write(buf, 0, in);
fis.close();
tos.closeEntry();
}
}
use of com.zimbra.cs.service.util.ItemData in project zm-mailbox by Zimbra.
the class ArchiveFormatter method saveItem.
private ArchiveOutputStream saveItem(UserServletContext context, MailItem mi, Map<Integer, String> fldrs, Map<Integer, Integer> cnts, boolean version, ArchiveOutputStream aos, CharsetEncoder charsetEncoder, Set<String> names) throws ServiceException {
String ext = null, name = null;
String extra = null;
Integer fid = mi.getFolderId();
String fldr;
InputStream is = null;
String metaParam = context.params.get(UserServlet.QP_META);
boolean meta = metaParam == null ? getDefaultMeta() : !metaParam.equals("0");
if (!version && mi.isTagged(Flag.FlagInfo.VERSIONED)) {
for (MailItem rev : context.targetMailbox.getAllRevisions(context.opContext, mi.getId(), mi.getType())) {
if (mi.getVersion() != rev.getVersion())
aos = saveItem(context, rev, fldrs, cnts, true, aos, charsetEncoder, names);
}
}
switch(mi.getType()) {
case APPOINTMENT:
Appointment appt = (Appointment) mi;
if (!appt.isPublic() && !appt.allowPrivateAccess(context.getAuthAccount(), context.isUsingAdminPrivileges())) {
return aos;
}
if (meta) {
name = appt.getSubject();
ext = "appt";
} else {
ext = "ics";
}
break;
case CHAT:
ext = "chat";
break;
case CONTACT:
Contact ct = (Contact) mi;
name = ct.getFileAsString();
if (!meta) {
ext = "vcf";
}
break;
case FLAG:
return aos;
case FOLDER:
case MOUNTPOINT:
case SEARCHFOLDER:
if (mi.getId() == Mailbox.ID_FOLDER_ROOT) {
name = "ROOT";
} else if (mi.getId() == Mailbox.ID_FOLDER_USER_ROOT) {
name = "USER_ROOT";
} else {
name = mi.getName();
}
break;
case MESSAGE:
Message msg = (Message) mi;
if (msg.hasCalendarItemInfos()) {
Set<ItemId> calItems = Sets.newHashSet();
for (Iterator<CalendarItemInfo> it = msg.getCalendarItemInfoIterator(); it.hasNext(); ) {
ItemId iid = it.next().getCalendarItemId();
if (iid != null) {
calItems.add(iid);
}
}
for (ItemId i : calItems) {
if (extra == null) {
extra = "calendar=" + i.toString();
} else {
extra += ',' + i.toString();
}
}
}
ext = "eml";
break;
case NOTE:
ext = "note";
break;
case TASK:
Task task = (Task) mi;
if (!task.isPublic() && !task.allowPrivateAccess(context.getAuthAccount(), context.isUsingAdminPrivileges())) {
return aos;
}
ext = "task";
break;
case VIRTUAL_CONVERSATION:
return aos;
case WIKI:
ext = "wiki";
break;
}
fldr = fldrs.get(fid);
if (fldr == null) {
Folder f = mi.getMailbox().getFolderById(context.opContext, fid);
cnts.put(fid, 1);
fldr = f.getPath();
if (fldr.startsWith("/")) {
fldr = fldr.substring(1);
}
fldr = sanitize(fldr, charsetEncoder);
fldr = ILLEGAL_FOLDER_CHARS.matcher(fldr).replaceAll("_");
fldrs.put(fid, fldr);
} else if (!(mi instanceof Folder)) {
final int BATCH = 500;
int cnt = cnts.get(fid) + 1;
cnts.put(fid, cnt);
cnt /= BATCH;
if (cnt > 0) {
fldr = fldr + '!' + cnt;
}
}
int targetBaseLength = 0;
if (context.noHierarchy()) {
// Parent hierarchy is not needed, so construct the folder names without parent hierarchy.
// e.g> represent "inbox/subfolder/target" as "target".
String targetPath = null;
if (context.itemPath.endsWith("/")) {
// inbox/subfolder/target/
targetPath = context.itemPath.substring(0, context.itemPath.lastIndexOf("/"));
} else {
// inbox/subfolder/target
targetPath = context.itemPath;
}
// "inbox/subfolder".length()
targetBaseLength = targetPath.lastIndexOf('/');
if (targetBaseLength >= fldr.length()) {
// fldr is "inbox/subfolder"
fldr = "";
} else if (targetBaseLength > 0) {
// fldr is "inbox/subfolder/target"
fldr = fldr.substring(targetBaseLength + 1);
}
}
try {
ArchiveOutputEntry aoe;
byte[] data = null;
String path = mi instanceof Contact ? getEntryName(mi, fldr, name, ext, charsetEncoder, names) : getEntryName(mi, fldr, name, ext, charsetEncoder, !(mi instanceof Document));
long miSize = mi.getSize();
if (miSize == 0 && mi.getDigest() != null) {
ZimbraLog.misc.debug("blob db size 0 for item %d", mi.getId());
return aos;
}
try {
is = mi.getContentStream();
} catch (Exception e) {
ZimbraLog.misc.error("missing blob for item %d: expected %d", mi.getId(), miSize);
return aos;
}
if (aos == null) {
aos = getOutputStream(context, charsetEncoder.charset().name());
}
if ((mi instanceof CalendarItem) && (context.getStartTime() != TIME_UNSPECIFIED || context.getEndTime() != TIME_UNSPECIFIED)) {
Collection<Instance> instances = ((CalendarItem) mi).expandInstances(context.getStartTime(), context.getEndTime(), false);
if (instances.isEmpty()) {
return aos;
}
}
aoe = aos.newOutputEntry(path + ".meta", mi.getType().toString(), mi.getType().toByte(), mi.getDate());
if (mi instanceof Message && (mi.getFlagBitmask() & Flag.ID_UNREAD) != 0) {
aoe.setUnread();
}
if (meta) {
ItemData itemData = new ItemData(mi, extra);
if (context.noHierarchy()) {
// itemData.path is of the form /Inbox/subfolder/target and after this step it becomes /target.
if (targetBaseLength > 0 && ((targetBaseLength + 1) < itemData.path.length())) {
itemData.path = itemData.path.substring(targetBaseLength + 1);
}
}
byte[] metaData = itemData.encode();
aoe.setSize(metaData.length);
aos.putNextEntry(aoe);
aos.write(metaData);
aos.closeEntry();
} else if (mi instanceof CalendarItem) {
Browser browser = HttpUtil.guessBrowser(context.req);
List<CalendarItem> calItems = new ArrayList<CalendarItem>();
boolean needAppleICalHacks = Browser.APPLE_ICAL.equals(browser);
boolean useOutlookCompatMode = Browser.IE.equals(browser);
OperationContext octxt = new OperationContext(context.getAuthAccount(), context.isUsingAdminPrivileges());
StringWriter writer = new StringWriter();
calItems.add((CalendarItem) mi);
context.targetMailbox.writeICalendarForCalendarItems(writer, octxt, calItems, useOutlookCompatMode, true, needAppleICalHacks, true);
data = writer.toString().getBytes(charsetEncoder.charset());
} else if (mi instanceof Contact) {
VCard vcf = VCard.formatContact((Contact) mi);
data = vcf.getFormatted().getBytes(charsetEncoder.charset());
} else if (mi instanceof Message) {
if (context.hasPart()) {
MimeMessage mm = ((Message) mi).getMimeMessage();
Set<String> attachmentNames = new HashSet<String>();
for (String part : context.getPart().split(",")) {
BufferStream bs;
MimePart mp = Mime.getMimePart(mm, part);
long sz;
if (mp == null) {
throw MailServiceException.NO_SUCH_PART(part);
}
name = Mime.getFilename(mp);
ext = null;
sz = mp.getSize();
if (sz == -1) {
sz = miSize;
}
if (name == null) {
name = "attachment";
} else {
int dot = name.lastIndexOf('.');
if (dot != -1 && dot < name.length() - 1) {
ext = name.substring(dot + 1);
name = name.substring(0, dot);
}
}
bs = new BufferStream(sz, 1024 * 1024);
InputStream stream = mp.getInputStream();
try {
bs.readFrom(stream);
} finally {
// close the stream, it could be an instance of PipedInputStream.
ByteUtil.closeStream(stream);
}
aoe = aos.newOutputEntry(getEntryName(mi, "", name, ext, charsetEncoder, attachmentNames), mi.getType().toString(), mi.getType().toByte(), mi.getDate());
sz = bs.getSize();
aoe.setSize(sz);
aos.putNextEntry(aoe);
bs.copyTo(aos.getOutputStream());
bs.close();
aos.closeEntry();
}
return aos;
}
}
aoe = aos.newOutputEntry(path, mi.getType().toString(), mi.getType().toByte(), mi.getDate());
if (data != null) {
aoe.setSize(data.length);
aos.putNextEntry(aoe);
aos.write(data);
aos.closeEntry();
} else if (is != null) {
if (context.shouldReturnBody()) {
byte[] buf = new byte[aos.getRecordSize() * 20];
int in;
long remain = miSize;
aoe.setSize(miSize);
aos.putNextEntry(aoe);
while (remain > 0 && (in = is.read(buf)) >= 0) {
aos.write(buf, 0, remain < in ? (int) remain : in);
remain -= in;
}
if (remain != 0) {
ZimbraLog.misc.error("mismatched blob size for item %d: expected %d", mi.getId(), miSize);
if (remain > 0) {
Arrays.fill(buf, (byte) ' ');
while (remain > 0) {
aos.write(buf, 0, remain < buf.length ? (int) remain : buf.length);
remain -= buf.length;
}
}
aos.closeEntry();
aoe = aos.newOutputEntry(path + ".err", mi.getType().toString(), mi.getType().toByte(), mi.getDate());
aoe.setSize(0);
aos.putNextEntry(aoe);
}
} else {
// Read headers into memory to compute size
byte[] headerData = HeadersOnlyInputStream.getHeaders(is);
aoe.setSize(headerData.length);
aos.putNextEntry(aoe);
aos.write(headerData);
}
aos.closeEntry();
}
} catch (Exception e) {
throw ServiceException.FAILURE("archive error", e);
} finally {
ByteUtil.closeStream(is);
}
return aos;
}
use of com.zimbra.cs.service.util.ItemData in project zm-mailbox by Zimbra.
the class ItemDataFile method extract.
public static void extract(InputStream is, boolean meta, Set<MailItem.Type> types, String cset, String dir) throws IOException {
byte[] buf = new byte[TarBuffer.DEFAULT_BLKSIZE];
TarEntry te;
TarInputStream tis = new TarInputStream(new GZIPInputStream(is), cset == null ? "UTF-8" : cset);
if (dir == null)
dir = ".";
try {
while ((te = tis.getNextEntry()) != null) {
if (skip(types, MailItem.Type.of((byte) te.getMajorDeviceId()))) {
continue;
}
File f = new File(dir + File.separator + te.getName());
FileOutputStream out;
if (!f.getParent().equals("."))
f.getParentFile().mkdir();
if (te.getName().endsWith(".meta")) {
if (!meta)
continue;
System.out.println(f);
out = new FileOutputStream(f);
ItemData id = new ItemData(getData(tis, te));
out.write(id.encode(2).getBytes("UTF-8"));
} else {
int in;
System.out.println(f);
out = new FileOutputStream(f);
while ((in = tis.read(buf)) != -1) out.write(buf, 0, in);
}
out.close();
f.setLastModified(te.getModTime().getTime());
}
} finally {
tis.close();
}
}
use of com.zimbra.cs.service.util.ItemData in project zm-mailbox by Zimbra.
the class ArchiveFormatter method saveCallback.
@Override
public void saveCallback(UserServletContext context, String contentType, Folder fldr, String file) throws IOException, ServiceException {
// Disable the jetty timeout
disableJettyTimeout(context);
Exception ex = null;
ItemData id = null;
FolderDigestInfo digestInfo = new FolderDigestInfo(context.opContext);
List<ServiceException> errs = new LinkedList<ServiceException>();
List<Folder> flist;
Map<Object, Folder> fmap = new HashMap<Object, Folder>();
Map<Integer, Integer> idMap = new HashMap<Integer, Integer>();
long last = System.currentTimeMillis();
String types = context.getTypesString();
String resolve = context.params.get(PARAM_RESOLVE);
String subfolder = context.params.get("subfolder");
String timestamp = context.params.get("timestamp");
String timeout = context.params.get("timeout");
try {
ArchiveInputStream ais;
int[] ids = null;
long interval = 45 * 1000;
Resolve r = resolve == null ? Resolve.Skip : Resolve.valueOf(resolve.substring(0, 1).toUpperCase() + resolve.substring(1).toLowerCase());
if (timeout != null) {
interval = Long.parseLong(timeout);
}
Set<MailItem.Type> searchTypes = null;
if (context.reqListIds != null) {
ids = context.reqListIds.clone();
Arrays.sort(ids);
}
if (!Strings.isNullOrEmpty(types)) {
try {
searchTypes = MailItem.Type.setOf(types);
} catch (IllegalArgumentException e) {
throw MailServiceException.INVALID_TYPE(e.getMessage());
}
searchTypes.remove(MailItem.Type.CONVERSATION);
}
Charset charset = context.getCharset();
try {
ais = getInputStream(context, charset.name());
} catch (Exception e) {
String filename = context.params.get(UserServlet.UPLOAD_NAME);
throw FormatterServiceException.INVALID_FORMAT(filename == null ? "unknown" : filename);
}
if (!Strings.isNullOrEmpty(subfolder)) {
fldr = createPath(context, fmap, fldr.getPath() + subfolder, Folder.Type.UNKNOWN);
}
flist = fldr.getSubfolderHierarchy();
if (r == Resolve.Reset) {
for (Folder f : flist) {
if (context.targetMailbox.isImmutableSystemFolder(f.getId()))
continue;
try {
List<Integer> delIds;
/* TODO Uncomment when bug 76892 is fixed.
if (System.currentTimeMillis() - last > interval) {
updateClient(context, true);
last = System.currentTimeMillis();
}
*/
if (searchTypes == null) {
delIds = context.targetMailbox.listItemIds(context.opContext, MailItem.Type.UNKNOWN, f.getId());
} else {
delIds = context.targetMailbox.getItemIds(context.opContext, f.getId()).getIds(searchTypes);
}
if (delIds == null)
continue;
int[] delIdsArray = new int[delIds.size()];
int i = 0;
for (Integer del : delIds) {
if (del >= Mailbox.FIRST_USER_ID) {
delIdsArray[i++] = del;
}
}
while (i < delIds.size()) {
delIdsArray[i++] = Mailbox.ID_AUTO_INCREMENT;
}
context.targetMailbox.delete(context.opContext, delIdsArray, MailItem.Type.UNKNOWN, null);
} catch (MailServiceException e) {
if (e.getCode() != MailServiceException.NO_SUCH_FOLDER) {
r = Resolve.Replace;
addError(errs, e);
}
} catch (Exception e) {
r = Resolve.Replace;
addError(errs, FormatterServiceException.UNKNOWN_ERROR(f.getName(), e));
}
}
context.targetMailbox.purge(MailItem.Type.UNKNOWN);
flist = fldr.getSubfolderHierarchy();
}
for (Folder f : flist) {
fmap.put(f.getId(), f);
fmap.put(f.getPath(), f);
}
try {
ArchiveInputEntry aie;
Boolean meta = false;
while ((aie = ais.getNextEntry()) != null) {
/* TODO Uncomment when bug 76892 is fixed.
if (System.currentTimeMillis() - last > interval) {
updateClient(context, true);
last = System.currentTimeMillis();
}
*/
if (aie.getName().startsWith("__MACOSX/")) {
continue;
} else if (aie.getName().endsWith(".meta")) {
meta = true;
if (id != null) {
addItem(context, fldr, fmap, digestInfo, idMap, ids, searchTypes, r, id, ais, null, errs);
}
try {
id = new ItemData(readArchiveEntry(ais, aie));
} catch (Exception e) {
addError(errs, FormatterServiceException.INVALID_FORMAT(aie.getName()));
}
continue;
} else if (aie.getName().endsWith(".err")) {
addError(errs, FormatterServiceException.MISMATCHED_SIZE(aie.getName()));
} else if (id == null) {
if (meta) {
addError(errs, FormatterServiceException.MISSING_META(aie.getName()));
} else {
addData(context, fldr, fmap, searchTypes, r, timestamp == null || !timestamp.equals("0"), ais, aie, errs);
}
} else if ((aie.getType() != 0 && id.ud.type != aie.getType()) || (id.ud.getBlobDigest() != null && aie.getSize() != -1 && id.ud.size != aie.getSize())) {
addError(errs, FormatterServiceException.MISMATCHED_META(aie.getName()));
} else {
addItem(context, fldr, fmap, digestInfo, idMap, ids, searchTypes, r, id, ais, aie, errs);
}
id = null;
}
if (id != null) {
addItem(context, fldr, fmap, digestInfo, idMap, ids, searchTypes, r, id, ais, null, errs);
}
} catch (Exception e) {
if (id == null) {
addError(errs, FormatterServiceException.UNKNOWN_ERROR(e));
} else {
addError(errs, FormatterServiceException.UNKNOWN_ERROR(id.path, e));
}
id = null;
} finally {
if (ais != null) {
ais.close();
}
contacts.clear();
}
} catch (Exception e) {
ex = e;
}
try {
updateClient(context, ex, errs);
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw ServiceException.FAILURE("Archive formatter failure", e);
}
}
Aggregations