use of com.zimbra.cs.mailbox.MailItem in project zm-mailbox by Zimbra.
the class VcfFormatter method formatCallback.
@Override
public void formatCallback(UserServletContext context) throws IOException, ServiceException {
Charset charset = context.getCharset();
Iterator<? extends MailItem> iterator = null;
try {
iterator = getMailItems(context, getDefaultStartTime(), getDefaultEndTime(), Integer.MAX_VALUE);
String filename = context.target instanceof Contact ? ((Contact) context.target).getFileAsString() : "contacts";
String cd = HttpUtil.createContentDisposition(context.req, Part.ATTACHMENT, filename + ".vcf");
context.resp.addHeader("Content-Disposition", cd);
// for backward compatibility
context.resp.setContentType(MimeConstants.CT_TEXT_VCARD_LEGACY);
context.resp.setCharacterEncoding(charset.name());
int count = 0;
while (iterator.hasNext()) {
MailItem item = iterator.next();
if (!(item instanceof Contact))
continue;
VCard vcf = VCard.formatContact((Contact) item);
context.resp.getOutputStream().write(vcf.getFormatted().getBytes(charset));
count++;
}
} finally {
if (iterator instanceof QueryResultIterator)
((QueryResultIterator) iterator).finished();
}
}
use of com.zimbra.cs.mailbox.MailItem in project zm-mailbox by Zimbra.
the class XmlFormatter method formatCallback.
@Override
public void formatCallback(UserServletContext context) throws ServiceException, IOException {
Element elt = getFactory().createElement("items");
ItemIdFormatter ifmt = new ItemIdFormatter(context.getAuthAccount(), context.targetMailbox, false);
Iterator<? extends MailItem> iterator = null;
try {
long start = context.getStartTime();
long end = context.getEndTime();
boolean hasTimeRange = start != TIME_UNSPECIFIED && end != TIME_UNSPECIFIED;
iterator = getMailItems(context, start, end, Integer.MAX_VALUE);
// this is lame
while (iterator.hasNext()) {
MailItem item = iterator.next();
if (item instanceof CalendarItem && hasTimeRange) {
// Skip appointments that have no instance in the time range.
CalendarItem calItem = (CalendarItem) item;
Collection<Instance> instances = calItem.expandInstances(start, end, false);
if (instances.isEmpty())
continue;
}
ToXML.encodeItem(elt, ifmt, context.opContext, item, ToXML.NOTIFY_FIELDS);
}
context.resp.getOutputStream().write(elt.toUTF8());
} finally {
if (iterator instanceof QueryResultIterator)
((QueryResultIterator) iterator).finished();
}
}
use of com.zimbra.cs.mailbox.MailItem in project zm-mailbox by Zimbra.
the class OctopusPatchFormatter method saveCallback.
// Formatter API
@Override
public void saveCallback(UserServletContext context, String contentType, Folder folder, String filename) throws IOException, ServiceException, UserServletException {
log.info("Uploading patch for " + filename);
if (filename == null) {
throw new UserServletException(HttpServletResponse.SC_BAD_REQUEST, "Missing filename");
}
MailItem item = null;
Mailbox mbox = folder.getMailbox();
try {
item = mbox.getItemByPath(context.opContext, filename, folder.getId());
if (!(item instanceof Document))
throw new UserServletException(HttpServletResponse.SC_BAD_REQUEST, "cannot overwrite existing object at that path");
} catch (NoSuchItemException e) {
log.debug("No document found at " + filename + "(folder id=" + folder.getId() + "; will create new one");
}
PatchInputStream pis = null;
PatchStore.IncomingPatch ip = null;
try {
ip = patchStore.createIncomingPatch(context.targetAccount.getId());
int defaultFileId = 0;
int defaultVersion = 0;
if (item != null) {
defaultFileId = item.getId();
defaultVersion = item.getVersion();
}
pis = PatchInputStream.create(context.getRequestInputStream(), // the current (target) user's view
context.targetMailbox, context.opContext, defaultFileId, defaultVersion, ip.getOutputStream(), ip.getManifest());
String creator = context.getAuthAccount() == null ? null : context.getAuthAccount().getName();
if (contentType == null) {
contentType = MimeDetect.getMimeDetect().detect(filename);
if (contentType == null)
contentType = MimeConstants.CT_APPLICATION_OCTET_STREAM;
}
log.debug("Storing blob");
Blob blob = StoreManager.getInstance().storeIncoming(pis);
log.debug("Creating parsed document; filename=" + filename + ", contentType=" + contentType + ", creator=" + creator);
ParsedDocument pd = new ParsedDocument(blob, filename, contentType, System.currentTimeMillis(), creator, context.req.getHeader("X-Zimbra-Description"), true);
log.debug("Parsed document created " + filename);
// scan upload for viruses
StringBuffer info = new StringBuffer();
UploadScanner.Result result = UploadScanner.acceptStream(pis, info);
if (result == UploadScanner.REJECT)
throw MailServiceException.UPLOAD_REJECTED(filename, info.toString());
if (result == UploadScanner.ERROR)
throw MailServiceException.SCAN_ERROR(filename);
if (item == null) {
log.debug("Creating new document " + filename);
item = mbox.createDocument(context.opContext, folder.getId(), pd, MailItem.Type.DOCUMENT, 0);
} else {
log.debug("Creating new version of the document " + filename + ", current version: " + item.getVersion());
item = mbox.addDocumentRevision(context.opContext, item.getId(), pd);
}
patchStore.acceptPatch(ip, item.getId(), item.getVersion());
NativeFormatter.sendZimbraHeaders(context, context.resp, item);
} catch (PatchException e) {
log.error("Patch upload failed: " + e);
patchStore.rejectPatch(ip);
throw new UserServletException(HttpServletResponse.SC_CONFLICT, "patch cannot be applied, try uploading whole file", e);
} finally {
try {
pis.close();
} catch (Exception e) {
log.error("Exception during PatchInputStream close, ignored: " + e);
}
}
}
use of com.zimbra.cs.mailbox.MailItem in project zm-mailbox by Zimbra.
the class WikiDigestFixup method getWikiDigests.
private static List<WikiDigest> getWikiDigests(int mboxId) throws IOException, ServiceException {
Mailbox mbox = null;
try {
mbox = MailboxManager.getInstance().getMailboxById(mboxId);
} catch (ServiceException e) {
String code = e.getCode();
if (AccountServiceException.NO_SUCH_ACCOUNT.equals(code) || ServiceException.WRONG_HOST.equals(code))
return null;
else
throw e;
}
OperationContext octxt = new OperationContext(mbox);
List<MailItem> items = new ArrayList<MailItem>();
List<MailItem> wikis = mbox.getItemList(octxt, MailItem.Type.WIKI);
if (wikis != null && wikis.size() > 0)
items.addAll(wikis);
List<MailItem> documents = mbox.getItemList(octxt, MailItem.Type.DOCUMENT);
if (documents != null && documents.size() > 0)
items.addAll(documents);
int len = items.size();
List<WikiDigest> list = new ArrayList<WikiDigest>(len);
if (len > 0) {
for (MailItem item : items) {
// didn't support >2GB wiki items when the bug was occurring
if (item.getSize() > Integer.MAX_VALUE)
continue;
int id = item.getId();
MailboxBlob blob = sStore.getMailboxBlob(item);
InputStream is = null;
try {
is = sStore.getContent(blob);
byte[] data = ByteUtil.getContent(is, (int) item.getSize());
String digest = ByteUtil.getSHA1Digest(data, true);
String currentDigest = item.getDigest();
if (!digest.equals(currentDigest)) {
System.out.println("Found id " + id + ", current digest = \"" + currentDigest + "\"");
WikiDigest wd = new WikiDigest(mboxId, id, digest);
list.add(wd);
} else {
System.out.println("Found id " + id + " but skipping because digest is correct.");
}
} finally {
ByteUtil.closeStream(is);
}
}
}
return list;
}
use of com.zimbra.cs.mailbox.MailItem in project zm-mailbox by Zimbra.
the class Sync method deltaSync.
private static String deltaSync(Element response, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox, SyncToken syncToken, int deleteLimit, int changeLimit, boolean typedDeletes, Folder root, Set<Folder> visible, int messageSyncStart) throws ServiceException {
int begin = syncToken.getChangeId();
int deleteModSeqCutoff = syncToken.getDeleteModSeq();
deleteModSeqCutoff = deleteModSeqCutoff <= 0 ? begin : deleteModSeqCutoff;
int mboxLastChangeId = mbox.getLastChangeID();
SyncToken newSyncToken = new SyncToken(mboxLastChangeId);
if (begin >= mboxLastChangeId && deleteModSeqCutoff >= mboxLastChangeId) {
return newSyncToken.toString();
}
int changeItemIdCutoff = syncToken.getOffsetInNext();
int deleteItemIdCutoff = syncToken.getDeleteOffsetInNext();
// first, fetch deleted items
TypedIdList tombstones = mbox.getTombstones(deleteModSeqCutoff);
Element eDeleted = response.addElement(MailConstants.E_DELETED);
// then, put together the requested folder hierarchy in 2 different flavors
List<Folder> hierarchy = (root == null || root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : root.getSubfolderHierarchy());
Set<Integer> targetIds = (root != null && root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : new HashSet<Integer>(hierarchy == null ? 0 : hierarchy.size()));
if (hierarchy != null) {
for (Folder folder : hierarchy) {
targetIds.add(folder.getId());
}
}
// then, handle created/modified folders
if (octxt.isDelegatedRequest(mbox)) {
// first, make sure that something changed...
if (!mbox.getModifiedFolders(begin).isEmpty() || !Collections.disjoint(tombstones.types(), FOLDER_TYPES)) {
// special-case the folder hierarchy for delegated delta sync
boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, -1, messageSyncStart, SyncPhase.DELTA);
// if no folders are visible, add an empty "<folder/>" as a hint
if (!anyFolders) {
response.addElement(MailConstants.E_FOLDER);
}
}
} else {
for (Folder folder : mbox.getModifiedFolders(begin)) {
// no case of "synthetic tombstone" (item falling out of the tree being synced)
if (targetIds == null || targetIds.contains(folder.getId())) {
ToXML.encodeFolder(response, ifmt, octxt, folder, Change.ALL_FIELDS);
} else {
tombstones.add(folder.getType(), folder.getId(), folder.getUuid(), folder.getModifiedSequence());
}
}
}
// next, handle created/modified tags
for (Tag tag : mbox.getModifiedTags(octxt, begin)) {
ToXML.encodeTag(response, ifmt, octxt, tag, Change.ALL_FIELDS);
}
// finally, handle created/modified "other items"
int itemCount = 0;
Pair<List<Integer>, TypedIdList> changed = mbox.getModifiedItems(octxt, Math.min(begin, deleteModSeqCutoff), messageSyncStart, MailItem.Type.UNKNOWN, targetIds, deleteModSeqCutoff);
List<Integer> modified = changed.getFirst();
// items that have been altered in non-visible folders will be returned as "deleted" in order to handle moves
if (changed.getSecond() != null) {
tombstones.addAll(changed.getSecond());
}
delta: while (!modified.isEmpty()) {
List<Integer> batch = modified.subList(0, Math.min(modified.size(), FETCH_BATCH_SIZE));
for (MailItem item : mbox.getItemById(octxt, batch, MailItem.Type.UNKNOWN)) {
// detect interrupted sync and resume from the appropriate place
if ((item.getModifiedSequence() == begin + 1 && item.getId() < changeItemIdCutoff) || item.getModifiedSequence() <= begin) {
//if interrupted delete and un-interrupted modifications.
continue;
}
// if we've overflowed this sync response, set things up so that a subsequent sync starts from where we're cutting off
if (itemCount >= changeLimit) {
response.addAttribute(MailConstants.A_QUERY_MORE, true);
newSyncToken.setChangeModSeq((item.getModifiedSequence() - 1));
newSyncToken.setChangeItemId(item.getId());
newSyncToken.setDeleteModSeq(mboxLastChangeId);
break delta;
}
// For items in the system, if the content has changed since the user last sync'ed
// (because it was edited or created), just send back the folder ID and saved date --
// the client will request the whole object out of band -- potentially using the
// content servlet's "include metadata in headers" hack.
// If it's just the metadata that changed, send back the set of mutable attributes.
boolean created = item.getSavedSequence() > begin;
ToXML.encodeItem(response, ifmt, octxt, item, created ? Change.FOLDER | Change.CONFLICT | Change.DATE | Change.PARENT : MUTABLE_FIELDS);
itemCount++;
}
batch.clear();
}
// cleanup: only return a <deleted> element if we're sending back deleted item ids
if ((deleteLimit > 0 && tombstones.size() > deleteLimit) || deleteItemIdCutoff > 0) {
PagedDelete pgDel = new PagedDelete(tombstones, typedDeletes);
pgDel.removeBeforeCutoff(deleteItemIdCutoff, deleteModSeqCutoff);
if (deleteLimit > 0) {
pgDel.trimDeletesTillPageLimit(deleteLimit);
}
encodePagedDelete(eDeleted, pgDel, newSyncToken, tombstones, typedDeletes);
if (pgDel.isDeleteOverFlow()) {
response.addAttribute(MailConstants.A_QUERY_MORE, true);
response.addAttribute(MailConstants.A_QUERY_MORE, true);
}
} else {
encodeUnpagedDelete(eDeleted, tombstones, typedDeletes);
}
return newSyncToken.toString();
}
Aggregations