Search in sources :

Example 21 with MailboxBlob

use of com.zimbra.cs.store.MailboxBlob in project zm-mailbox by Zimbra.

the class Mailbox method addMessageInternal.

private Message addMessageInternal(OperationContext octxt, ParsedMessage pm, int folderId, boolean noICal, int flags, String[] tags, int conversationId, String rcptEmail, Message.DraftInfo dinfo, CustomMetadata customData, DeliveryContext dctxt, StagedBlob staged) throws IOException, ServiceException {
    assert lock.isWriteLockedByCurrentThread();
    if (pm == null) {
        throw ServiceException.INVALID_REQUEST("null ParsedMessage when adding message to mailbox " + mId, null);
    }
    if (Math.abs(conversationId) <= HIGHEST_SYSTEM_ID) {
        conversationId = ID_AUTO_INCREMENT;
    }
    CreateMessage redoPlayer = (octxt == null ? null : (CreateMessage) octxt.getPlayer());
    boolean needRedo = needRedo(octxt, redoPlayer);
    boolean isRedo = redoPlayer != null;
    Blob blob = dctxt.getIncomingBlob();
    if (blob == null) {
        throw ServiceException.FAILURE("Incoming blob not found.", null);
    }
    // make sure we're parsing headers using the target account's charset
    pm.setDefaultCharset(getAccount().getPrefMailDefaultCharset());
    // quick check to make sure we don't deliver 5 copies of the same message
    String msgidHeader = pm.getMessageID();
    boolean isSent = ((flags & Flag.BITMASK_FROM_ME) != 0);
    if (!isRedo && msgidHeader != null && !isSent && mSentMessageIDs.containsKey(msgidHeader)) {
        Integer sentMsgID = mSentMessageIDs.get(msgidHeader);
        if (conversationId == ID_AUTO_INCREMENT) {
            conversationId = getConversationIdFromReferent(pm.getMimeMessage(), sentMsgID.intValue());
            ZimbraLog.mailbox.debug("duplicate detected but not deduped (%s); will try to slot into conversation %d", msgidHeader, conversationId);
        }
    }
    // caller can't set system flags other than \Draft, \Sent and \Post
    flags &= ~Flag.FLAGS_SYSTEM | Flag.BITMASK_DRAFT | Flag.BITMASK_FROM_ME | Flag.BITMASK_POST;
    // caller can't specify non-message flags
    flags &= Flag.FLAGS_GENERIC | Flag.FLAGS_MESSAGE;
    String digest;
    int msgSize;
    try {
        digest = blob.getDigest();
        msgSize = (int) blob.getRawSize();
    } catch (IOException e) {
        throw ServiceException.FAILURE("Unable to get message properties.", e);
    }
    CreateMessage redoRecorder = new CreateMessage(mId, rcptEmail, pm.getReceivedDate(), dctxt.getShared(), digest, msgSize, folderId, noICal, flags, tags, customData);
    StoreIncomingBlob storeRedoRecorder = null;
    // strip out unread flag for internal storage (don't do this before redoRecorder initialization)
    boolean unread = (flags & Flag.BITMASK_UNREAD) > 0;
    flags &= ~Flag.BITMASK_UNREAD;
    // "having attachments" is currently tracked via flags
    if (pm.hasAttachments()) {
        flags |= Flag.BITMASK_ATTACHED;
    } else {
        flags &= ~Flag.BITMASK_ATTACHED;
    }
    // priority is calculated from headers
    flags &= ~(Flag.BITMASK_HIGH_PRIORITY | Flag.BITMASK_LOW_PRIORITY);
    flags |= pm.getPriorityBitmask();
    boolean isSpam = folderId == ID_FOLDER_SPAM;
    boolean isDraft = (flags & Flag.BITMASK_DRAFT) != 0;
    // draft replies get slotted in the same conversation as their parent, if possible
    if (isDraft && !isRedo && conversationId == ID_AUTO_INCREMENT && dinfo != null && !Strings.isNullOrEmpty(dinfo.origId)) {
        try {
            ItemId iid = new ItemId(dinfo.origId, getAccountId());
            if (iid.getId() > 0 && iid.belongsTo(this)) {
                conversationId = getMessageById(octxt, iid.getId()).getConversationId();
            }
        } catch (ServiceException e) {
        }
    }
    Message msg = null;
    boolean success = false;
    CustomMetadata.CustomMetadataList extended = MetadataCallback.preDelivery(pm);
    if (customData != null) {
        if (extended == null) {
            extended = customData.asList();
        } else {
            extended.addSection(customData);
        }
    }
    Threader threader = pm.getThreader(this);
    String subject = pm.getNormalizedSubject();
    try {
        beginTransaction("addMessage", octxt, redoRecorder);
        if (isRedo) {
            rcptEmail = redoPlayer.getRcptEmail();
        }
        Tag.NormalizedTags ntags = new Tag.NormalizedTags(this, tags);
        Folder folder = getFolderById(folderId);
        // step 0: preemptively check for quota issues (actual update is done in Message.create)
        if (!getAccount().isMailAllowReceiveButNotSendWhenOverQuota()) {
            checkSizeChange(getSize() + staged.getSize());
        }
        // step 1: get an ID assigned for the new message
        int messageId = getNextItemId(!isRedo ? ID_AUTO_INCREMENT : redoPlayer.getMessageId());
        List<Conversation> mergeConvs = null;
        if (isRedo) {
            conversationId = redoPlayer.getConvId();
            // fetch the conversations that were merged in as a result of the original delivery...
            List<Integer> mergeConvIds = redoPlayer.getMergedConvIds();
            mergeConvs = new ArrayList<Conversation>(mergeConvIds.size());
            for (int mergeId : mergeConvIds) {
                try {
                    mergeConvs.add(getConversationById(mergeId));
                } catch (NoSuchItemException nsie) {
                    ZimbraLog.mailbox.debug("could not find merge conversation %d", mergeId);
                }
            }
        }
        // step 2: figure out where the message belongs
        Conversation conv = null;
        if (threader.isEnabled()) {
            boolean isReply = pm.isReply();
            if (conversationId != ID_AUTO_INCREMENT) {
                try {
                    // fetch the requested conversation
                    //   (we'll ensure that it's receiving new mail after the new message is added to it)
                    conv = getConversationById(conversationId);
                    ZimbraLog.mailbox.debug("fetched explicitly-specified conversation %d", conv.getId());
                } catch (NoSuchItemException nsie) {
                    if (!isRedo) {
                        ZimbraLog.mailbox.debug("could not find explicitly-specified conversation %d", conversationId);
                        conversationId = ID_AUTO_INCREMENT;
                    }
                }
            } else if (!isRedo && !isSpam && (isReply || (!isSent && !subject.isEmpty()))) {
                List<Conversation> matches = threader.lookupConversation();
                if (matches != null && !matches.isEmpty()) {
                    // file the message into the largest conversation, then later merge any other matching convs
                    Collections.sort(matches, new MailItem.SortSizeDescending());
                    conv = matches.remove(0);
                    mergeConvs = matches;
                }
            }
        }
        if (conv != null && conv.isTagged(Flag.FlagInfo.MUTED)) {
            // adding a message to a muted conversation marks it muted and read
            unread = false;
            flags |= Flag.BITMASK_MUTED;
        }
        // step 3: create the message and update the cache
        //         and if the message is also an invite, deal with the calendar item
        Conversation convTarget = conv instanceof VirtualConversation ? null : conv;
        if (convTarget != null) {
            ZimbraLog.mailbox.debug("  placing message in existing conversation %d", convTarget.getId());
        }
        CalendarPartInfo cpi = pm.getCalendarPartInfo();
        ZVCalendar iCal = null;
        if (cpi != null && CalendarItem.isAcceptableInvite(getAccount(), cpi)) {
            iCal = cpi.cal;
        }
        msg = Message.create(messageId, folder, convTarget, pm, staged, unread, flags, ntags, dinfo, noICal, iCal, extended);
        redoRecorder.setMessageId(msg.getId());
        // step 4: create a conversation for the message, if necessary
        if (threader.isEnabled() && convTarget == null) {
            if (conv == null && conversationId == ID_AUTO_INCREMENT) {
                conv = VirtualConversation.create(this, msg);
                ZimbraLog.mailbox.debug("placed message %d in vconv %d", msg.getId(), conv.getId());
                redoRecorder.setConvFirstMsgId(-1);
            } else {
                Message[] contents = null;
                VirtualConversation vconv = null;
                if (!isRedo) {
                    vconv = (VirtualConversation) conv;
                    contents = (vconv == null ? new Message[] { msg } : new Message[] { vconv.getMessage(), msg });
                } else {
                    // Executing redo.
                    int convFirstMsgId = redoPlayer.getConvFirstMsgId();
                    Message convFirstMsg = null;
                    // If there was a virtual conversation, then...
                    if (convFirstMsgId > 0) {
                        try {
                            convFirstMsg = getMessageById(octxt, redoPlayer.getConvFirstMsgId());
                        } catch (MailServiceException e) {
                            if (!MailServiceException.NO_SUCH_MSG.equals(e.getCode())) {
                                throw e;
                            }
                        // The first message of conversation may have been deleted
                        // by user between the time of original operation and redo.
                        // Handle the case by skipping the updating of its
                        // conversation ID.
                        }
                        // if it is still a standalone message.
                        if (convFirstMsg != null && convFirstMsg.getConversationId() < 0) {
                            contents = new Message[] { convFirstMsg, msg };
                            vconv = new VirtualConversation(this, convFirstMsg);
                        }
                    }
                    if (contents == null) {
                        contents = new Message[] { msg };
                    }
                }
                redoRecorder.setConvFirstMsgId(vconv != null ? vconv.getMessageId() : -1);
                conv = createConversation(conversationId, contents);
                if (vconv != null) {
                    ZimbraLog.mailbox.debug("removed vconv %d", vconv.getId());
                    vconv.removeChild(vconv.getMessage());
                }
                //   associate the first message's reference hashes with the new conversation
                if (contents.length == 2) {
                    threader.changeThreadingTargets(contents[0], conv);
                }
            }
        } else {
            // conversation feature turned off
            redoRecorder.setConvFirstMsgId(-1);
        }
        redoRecorder.setConvId(conv != null && !(conv instanceof VirtualConversation) ? conv.getId() : -1);
        // if we're threading by references, associate the new message's reference hashes with its conversation
        if (!isSpam && !isDraft) {
            threader.recordAddedMessage(conv);
        }
        if (conv != null && mergeConvs != null) {
            redoRecorder.setMergedConversations(mergeConvs);
            for (Conversation smaller : mergeConvs) {
                ZimbraLog.mailbox.info("merging conversation %d for references threading", smaller.getId());
                //                    try {
                conv.merge(smaller);
            //                    } catch (ServiceException e) {
            //                        if (!e.getCode().equals(MailServiceException.NO_SUCH_MSG)) {
            //                            throw e;
            //                        }
            //                    }
            }
        }
        // conversations may have shifted, so the threader's cached state is now questionable
        threader.reset();
        // step 5: write the redolog entries
        if (dctxt.getShared()) {
            if (dctxt.isFirst() && needRedo) {
                // Log entry in redolog for blob save.  Blob bytes are logged in the StoreIncoming entry.
                // Subsequent CreateMessage ops will reference this blob.
                storeRedoRecorder = new StoreIncomingBlob(digest, msgSize, dctxt.getMailboxIdList());
                storeRedoRecorder.start(getOperationTimestampMillis());
                storeRedoRecorder.setBlobBodyInfo(blob.getFile());
                storeRedoRecorder.log();
            }
            // Link to the file created by StoreIncomingBlob.
            redoRecorder.setMessageLinkInfo(blob.getPath());
        } else {
            // Store the blob data inside the CreateMessage op.
            redoRecorder.setMessageBodyInfo(blob.getFile());
        }
        // step 6: link to existing blob
        MailboxBlob mblob = StoreManager.getInstance().link(staged, this, messageId, getOperationChangeID());
        markOtherItemDirty(mblob);
        // when we created the Message, we used the staged locator/size/digest;
        //   make sure that data actually matches the final blob in the store
        msg.updateBlobData(mblob);
        if (dctxt.getMailboxBlob() == null) {
            // Set mailbox blob for in case we want to add the message to the
            // message cache after delivery.
            dctxt.setMailboxBlob(mblob);
        }
        // step 7: queue new message for indexing
        index.add(msg);
        success = true;
        // step 8: send lawful intercept message
        try {
            Notification.getInstance().interceptIfNecessary(this, pm.getMimeMessage(), "add message", folder);
        } catch (ServiceException e) {
            ZimbraLog.mailbox.error("unable to send legal intercept message", e);
        }
    } finally {
        if (storeRedoRecorder != null) {
            if (success) {
                storeRedoRecorder.commit();
            } else {
                storeRedoRecorder.abort();
            }
        }
        endTransaction(success);
        if (success) {
            // Everything worked.  Update the blob field in ParsedMessage
            // so the next recipient in the multi-recipient case will link
            // to this blob as opposed to saving its own copy.
            dctxt.setFirst(false);
        }
    }
    // step 8: remember the Message-ID header so that we can avoid receiving duplicates
    if (isSent && !isRedo && msgidHeader != null) {
        mSentMessageIDs.put(msgidHeader, msg.getId());
    }
    return msg;
}
Also used : ImapMessage(com.zimbra.cs.imap.ImapMessage) Pop3Message(com.zimbra.cs.pop3.Pop3Message) MimeMessage(javax.mail.internet.MimeMessage) CreateMessage(com.zimbra.cs.redolog.op.CreateMessage) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) NormalizedTags(com.zimbra.cs.mailbox.Tag.NormalizedTags) CreateFolder(com.zimbra.cs.redolog.op.CreateFolder) ZFolder(com.zimbra.client.ZFolder) CalendarPartInfo(com.zimbra.cs.mime.ParsedMessage.CalendarPartInfo) ItemId(com.zimbra.cs.service.util.ItemId) ZVCalendar(com.zimbra.common.calendar.ZCalendar.ZVCalendar) StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) LinkedList(java.util.LinkedList) ArrayList(java.util.ArrayList) List(java.util.List) TypedIdList(com.zimbra.cs.mailbox.util.TypedIdList) StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) StagedBlob(com.zimbra.cs.store.StagedBlob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) Blob(com.zimbra.cs.store.Blob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) IOException(java.io.IOException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) RefreshMountpoint(com.zimbra.cs.redolog.op.RefreshMountpoint) TargetConstraint(com.zimbra.cs.mailbox.MailItem.TargetConstraint) CreateMountpoint(com.zimbra.cs.redolog.op.CreateMountpoint) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) NormalizedTags(com.zimbra.cs.mailbox.Tag.NormalizedTags) CreateMessage(com.zimbra.cs.redolog.op.CreateMessage) AlterItemTag(com.zimbra.cs.redolog.op.AlterItemTag) CreateTag(com.zimbra.cs.redolog.op.CreateTag) DbTag(com.zimbra.cs.db.DbTag) CustomMetadata(com.zimbra.cs.mailbox.MailItem.CustomMetadata)

Example 22 with MailboxBlob

use of com.zimbra.cs.store.MailboxBlob in project zm-mailbox by Zimbra.

the class Mailbox method addDocumentRevision.

public Document addDocumentRevision(OperationContext octxt, int docId, ParsedDocument pd) throws IOException, ServiceException {
    StoreManager sm = StoreManager.getInstance();
    StagedBlob staged = sm.stage(pd.getBlob(), this);
    AddDocumentRevision redoRecorder = new AddDocumentRevision(mId, pd.getDigest(), pd.getSize(), 0);
    boolean success = false;
    try {
        beginTransaction("addDocumentRevision", octxt, redoRecorder);
        Document doc = getDocumentById(docId);
        redoRecorder.setDocument(pd);
        redoRecorder.setDocId(docId);
        redoRecorder.setItemType(doc.getType());
        // TODO: simplify the redoRecorder by not subclassing from CreateMessage
        // Get the redolog data from the mailbox blob.  This is less than ideal in the
        // HTTP store case because it will result in network access, and possibly an
        // extra write to local disk.  If this becomes a problem, we should update the
        // ParsedDocument constructor to take a DataSource instead of an InputStream.
        MailboxBlob mailboxBlob = doc.setContent(staged, pd);
        redoRecorder.setMessageBodyInfo(new MailboxBlobDataSource(mailboxBlob), mailboxBlob.getSize());
        index.add(doc);
        success = true;
        return doc;
    } catch (IOException ioe) {
        throw ServiceException.FAILURE("error writing document blob", ioe);
    } finally {
        endTransaction(success);
        sm.quietDelete(staged);
    }
}
Also used : StagedBlob(com.zimbra.cs.store.StagedBlob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) MailboxBlobDataSource(com.zimbra.cs.store.MailboxBlobDataSource) AddDocumentRevision(com.zimbra.cs.redolog.op.AddDocumentRevision) IOException(java.io.IOException) ParsedDocument(com.zimbra.cs.mime.ParsedDocument) IndexDocument(com.zimbra.cs.index.IndexDocument) SaveDocument(com.zimbra.cs.redolog.op.SaveDocument) StoreManager(com.zimbra.cs.store.StoreManager)

Example 23 with MailboxBlob

use of com.zimbra.cs.store.MailboxBlob in project zm-mailbox by Zimbra.

the class DbMailItem method accumulateLeafNodes.

/**
     * Accumulates <tt>PendingDelete</tt> info for the given <tt>ResultSet</tt>.
     * @return a <tt>List</tt> of all versioned items, to be used in a subsequent call to
     * {@link DbMailItem#accumulateLeafRevisions}, or an empty list.
     */
static List<Integer> accumulateLeafNodes(PendingDelete info, Mailbox mbox, ResultSet rs) throws SQLException, ServiceException {
    boolean dumpsterEnabled = mbox.dumpsterEnabled();
    boolean useDumpsterForSpam = mbox.useDumpsterForSpam();
    StoreManager sm = StoreManager.getInstance();
    List<Integer> versioned = new ArrayList<Integer>();
    while (rs.next()) {
        // first check to make sure we don't have a modify conflict
        int revision = rs.getInt(LEAF_CI_MOD_CONTENT);
        int modMetadata = rs.getInt(LEAF_CI_MOD_METADATA);
        if (!mbox.checkItemChangeID(modMetadata, revision)) {
            info.incomplete = true;
            continue;
        }
        int id = rs.getInt(LEAF_CI_ID);
        String uuid = rs.getString(LEAF_CI_UUID);
        long size = rs.getLong(LEAF_CI_SIZE);
        MailItem.Type type = MailItem.Type.of(rs.getByte(LEAF_CI_TYPE));
        Integer itemId = Integer.valueOf(id);
        info.itemIds.add(type, itemId, uuid);
        info.size += size;
        if (rs.getBoolean(LEAF_CI_IS_UNREAD)) {
            info.unreadIds.add(itemId);
        }
        boolean isMessage = false;
        switch(type) {
            case CONTACT:
                info.contacts++;
                break;
            case CHAT:
            case MESSAGE:
                isMessage = true;
                break;
        }
        // record deleted virtual conversations and modified-or-deleted real conversations
        if (isMessage) {
            int parentId = rs.getInt(LEAF_CI_PARENT_ID);
            if (rs.wasNull() || parentId <= 0) {
                // conversations don't have UUIDs, so this is safe
                info.itemIds.add(MailItem.Type.VIRTUAL_CONVERSATION, -id, null);
            } else {
                info.modifiedIds.add(parentId);
            }
        }
        int flags = rs.getInt(LEAF_CI_FLAGS);
        if ((flags & Flag.BITMASK_VERSIONED) != 0) {
            versioned.add(id);
        }
        Integer folderId = rs.getInt(LEAF_CI_FOLDER_ID);
        boolean isDeleted = (flags & Flag.BITMASK_DELETED) != 0;
        LocationCount fcount = info.folderCounts.get(folderId);
        if (fcount == null) {
            info.folderCounts.put(folderId, new LocationCount(1, isDeleted ? 1 : 0, size));
        } else {
            fcount.increment(1, isDeleted ? 1 : 0, size);
        }
        String[] tags = DbTag.deserializeTags(rs.getString(LEAF_CI_TAGS));
        if (tags != null) {
            for (String tag : tags) {
                LocationCount tcount = info.tagCounts.get(tag);
                if (tcount == null) {
                    info.tagCounts.put(tag, new LocationCount(1, isDeleted ? 1 : 0, size));
                } else {
                    tcount.increment(1, isDeleted ? 1 : 0, size);
                }
            }
        }
        int fid = folderId != null ? folderId.intValue() : -1;
        if (!dumpsterEnabled || fid == Mailbox.ID_FOLDER_DRAFTS || (fid == Mailbox.ID_FOLDER_SPAM && !useDumpsterForSpam)) {
            String blobDigest = rs.getString(LEAF_CI_BLOB_DIGEST);
            if (blobDigest != null) {
                info.blobDigests.add(blobDigest);
                String locator = rs.getString(LEAF_CI_LOCATOR);
                try {
                    MailboxBlob mblob = sm.getMailboxBlob(mbox, id, revision, locator, false);
                    if (mblob == null) {
                        ZimbraLog.mailbox.warn("missing blob for id: %d, change: %d", id, revision);
                    } else {
                        info.blobs.add(mblob);
                    }
                } catch (Exception e1) {
                    ZimbraLog.mailbox.warn("Exception while getting mailbox blob", e1);
                }
            }
            int indexId = rs.getInt(LEAF_CI_INDEX_ID);
            boolean indexed = !rs.wasNull();
            if (indexed) {
                if (info.sharedIndex == null) {
                    info.sharedIndex = new HashSet<Integer>();
                }
                boolean shared = (flags & Flag.BITMASK_COPIED) != 0;
                if (shared) {
                    info.sharedIndex.add(indexId);
                } else {
                    info.indexIds.add(indexId > MailItem.IndexStatus.STALE.id() ? indexId : id);
                }
            }
        }
    }
    return versioned;
}
Also used : MailboxBlob(com.zimbra.cs.store.MailboxBlob) ArrayList(java.util.ArrayList) EncoderException(org.apache.commons.codec.EncoderException) DecoderException(org.apache.commons.codec.DecoderException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) ServiceException(com.zimbra.common.service.ServiceException) SQLException(java.sql.SQLException) IOException(java.io.IOException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) StoreManager(com.zimbra.cs.store.StoreManager) MailItem(com.zimbra.cs.mailbox.MailItem)

Example 24 with MailboxBlob

use of com.zimbra.cs.store.MailboxBlob in project zm-mailbox by Zimbra.

the class MailItem method setContent.

MailboxBlob setContent(StagedBlob staged, Object content) throws ServiceException, IOException {
    addRevision(false);
    // update the item's relevant attributes
    markItemModified(Change.CONTENT | Change.DATE | Change.IMAP_UID | Change.SIZE);
    // delete the old blob *unless* we've already rewritten it in this transaction
    if (getSavedSequence() != mMailbox.getOperationChangeID()) {
        if (!canAccess(ACL.RIGHT_WRITE)) {
            throw ServiceException.PERM_DENIED("you do not have the necessary permissions on the item");
        }
        boolean delete = true;
        // don't delete blob if last revision uses it
        if (isTagged(Flag.FlagInfo.VERSIONED)) {
            List<MailItem> revisions = loadRevisions();
            if (!revisions.isEmpty()) {
                MailItem lastRev = revisions.get(revisions.size() - 1);
                if (lastRev.getSavedSequence() == getSavedSequence()) {
                    delete = false;
                }
            }
        }
        if (delete) {
            markBlobForDeletion();
        }
    }
    // remove the content from the cache
    MessageCache.purge(this);
    // update the object to reflect its new contents
    long size = staged == null ? 0 : staged.getSize();
    if (mData.size != size) {
        mMailbox.updateSize(size - mData.size, isQuotaCheckRequired());
        mData.size = size;
    }
    getFolder().updateSize(0, 0, size - mData.size);
    mData.setBlobDigest(staged == null ? null : staged.getDigest());
    mData.date = mMailbox.getOperationTimestamp();
    mData.imapId = mMailbox.isTrackingImap() ? 0 : mData.id;
    contentChanged();
    // write the content (if any) to the store
    MailboxBlob mblob = null;
    if (staged != null) {
        StoreManager sm = StoreManager.getInstance();
        // under windows, a rename will fail if the incoming file is open
        if (SystemUtil.ON_WINDOWS)
            mblob = sm.link(staged, mMailbox, mId, getSavedSequence());
        else
            mblob = sm.renameTo(staged, mMailbox, mId, getSavedSequence());
        mMailbox.markOtherItemDirty(mblob);
    }
    mBlob = null;
    mData.locator = mblob == null ? null : mblob.getLocator();
    // rewrite the DB row to reflect our new view (MUST call saveData)
    reanalyze(content, size);
    return mblob;
}
Also used : DbMailItem(com.zimbra.cs.db.DbMailItem) MailboxBlob(com.zimbra.cs.store.MailboxBlob) StoreManager(com.zimbra.cs.store.StoreManager)

Example 25 with MailboxBlob

use of com.zimbra.cs.store.MailboxBlob in project zm-mailbox by Zimbra.

the class MailItem method icopy.

/** Copies the item to the target folder.  Persists the new item to the
     *  database and the in-memory cache.  Copies to the same folder as the
     *  original item will succeed, but it is strongly suggested that
     *  {@link #copy(Folder, int, int, short)} be used in that case.<p>
     *
     *  Immutable copied items (both the original and the target) share the
     *  same entry in the index and get the {@link Flag#BITMASK_COPIED} flag to
     *  facilitate garbage collection of index entries.  (Mutable copied items
     *  are indexed separately.)  They do not share the same blob on disk,
     *  although the system will use a hard link where possible.  Copied
     *  {@link Message}s are remain in the same {@link Conversation}, but the
     *  <b>original</b> Message is placed in a new {@link VirtualConversation}
     *  rather than being grouped with the copied Message.
     *
     * @param target  The folder to copy the item to.
     * @param copyId  The item id for the newly-created copy.
     * @perms {@link ACL#RIGHT_INSERT} on the target folder,
     *        {@link ACL#RIGHT_READ} on the original item
     * @throws ServiceException  The following error codes are possible:<ul>
     *    <li><tt>mail.CANNOT_COPY</tt> - if the item is not copyable
     *    <li><tt>mail.CANNOT_CONTAIN</tt> - if the target folder can't hold
     *        the copy of the item
     *    <li><tt>service.FAILURE</tt> - if there's a database failure
     *    <li><tt>service.PERM_DENIED</tt> - if you don't have sufficient
     *        permissions</ul> */
MailItem icopy(Folder target, int copyId, String copyUuid) throws IOException, ServiceException {
    if (!isCopyable())
        throw MailServiceException.CANNOT_COPY(mId);
    if (!target.canContain(this))
        throw MailServiceException.CANNOT_CONTAIN();
    // permissions required are the same as for copy()
    if (!canAccess(ACL.RIGHT_READ))
        throw ServiceException.PERM_DENIED("you do not have the required rights on the item");
    if (!target.canAccess(ACL.RIGHT_INSERT))
        throw ServiceException.PERM_DENIED("you do not have the required rights on the target folder");
    // fetch the parent *before* changing the DB
    MailItem parent = getParent();
    // first, copy the item to the target folder while setting:
    //   - FLAGS -> FLAGS | Flag.BITMASK_COPIED
    //   - INDEX_ID -> old index id
    //   - FOLDER_ID -> new folder
    //   - IMAP_ID -> new IMAP uid
    //   - VOLUME_ID -> target volume ID
    // then, update the original item
    //   - PARENT_ID -> NULL
    //   - FLAGS -> FLAGS | Flag.BITMASK_COPIED
    // finally, update OPEN_CONVERSATION if PARENT_ID was NULL
    //   - ITEM_ID = copy's id for hash
    String locator = null;
    MailboxBlob srcMblob = getBlob();
    if (srcMblob != null) {
        StoreManager sm = StoreManager.getInstance();
        MailboxBlob mblob = sm.copy(srcMblob, mMailbox, copyId, mMailbox.getOperationChangeID());
        mMailbox.markOtherItemDirty(mblob);
        locator = mblob.getLocator();
    }
    // We'll share the index entry if this item can't change out from under us. Re-index the copy if existing item
    // (a) wasn't indexed or (b) is mutable.
    boolean shareIndex = !isMutable() && getIndexStatus() == IndexStatus.DONE && !target.inSpam();
    UnderlyingData data = mData.duplicate(copyId, copyUuid, target.getId(), locator);
    data.metadata = encodeMetadata().toString();
    data.imapId = copyId;
    data.indexId = shareIndex ? getIndexId() : IndexStatus.DEFERRED.id();
    data.contentChanged(mMailbox);
    ZimbraLog.mailop.info("Performing IMAP copy of %s: copyId=%d, folderId=%d, folderName=%s, parentId=%d.", getMailopContext(this), copyId, target.getId(), target.getName(), data.parentId);
    DbMailItem.icopy(this, data, shareIndex);
    MailItem copy = constructItem(mMailbox, data);
    copy.finishCreation(null);
    if (shareIndex && !isTagged(Flag.FlagInfo.COPIED)) {
        Flag copiedFlag = mMailbox.getFlagById(Flag.ID_COPIED);
        tagChanged(copiedFlag, true);
        copy.tagChanged(copiedFlag, true);
        if (parent != null)
            parent.inheritedTagChanged(copiedFlag, true);
    }
    if (parent != null && parent.getId() > 0) {
        markItemModified(Change.PARENT);
        parent.markItemModified(Change.CHILDREN);
        mData.parentId = mData.type == Type.MESSAGE.toByte() ? -mId : -1;
        metadataChanged();
    }
    if (!shareIndex) {
        mMailbox.index.add(copy);
    }
    return copy;
}
Also used : DbMailItem(com.zimbra.cs.db.DbMailItem) MailboxBlob(com.zimbra.cs.store.MailboxBlob) StoreManager(com.zimbra.cs.store.StoreManager)

Aggregations

MailboxBlob (com.zimbra.cs.store.MailboxBlob)27 StoreManager (com.zimbra.cs.store.StoreManager)16 StagedBlob (com.zimbra.cs.store.StagedBlob)14 Mailbox (com.zimbra.cs.mailbox.Mailbox)12 Blob (com.zimbra.cs.store.Blob)12 IOException (java.io.IOException)11 ServiceException (com.zimbra.common.service.ServiceException)8 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)7 Test (org.junit.Test)7 ArrayList (java.util.ArrayList)6 InputStream (java.io.InputStream)5 TargetConstraint (com.zimbra.cs.mailbox.MailItem.TargetConstraint)4 CreateMountpoint (com.zimbra.cs.redolog.op.CreateMountpoint)4 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 DbMailItem (com.zimbra.cs.db.DbMailItem)3 DbTag (com.zimbra.cs.db.DbTag)3 IndexDocument (com.zimbra.cs.index.IndexDocument)3 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)3 Message (com.zimbra.cs.mailbox.Message)3 NormalizedTags (com.zimbra.cs.mailbox.Tag.NormalizedTags)3