processPartStat(newInvite, pm != null ? pm.getMimeMessage() : null, false, newInvite.getPartStat());
callProcessPartStat = true;
    private void processNewInviteRequestOrCancel(ZOrganizer originalOrganizer,                                                 ParsedMessage pm,                                                 Invite newInvite,                                                 boolean force,                                                 int folderId,                                                 short volumeId)    throws ServiceException {        String method = newInvite.getMethod();        // Remove everyone that is made obselete by this request        boolean addNewOne = true;        boolean isCancel = method.equals(ICalTok.CANCEL.toString());        if (!canAccess(isCancel ? ACL.RIGHT_DELETE : ACL.RIGHT_WRITE))            throw ServiceException.PERM_DENIED("you do not have sufficient permissions on this appointment");        // If we're doing a modify rather than cancel, make sure the organizer        // in the new Invite is the same as the original organizer.        if (!isCancel) {            if (newInvite.hasOrganizer()) {                String newOrgAddr = newInvite.getOrganizer().getAddress();                if (originalOrganizer == null)                    throw ServiceException.INVALID_REQUEST(                            "Changing organizer of an appointment is not allowed: old=(unspecified), new=" + newOrgAddr, null);                String origOrgAddr = originalOrganizer.getAddress();                if (!newOrgAddr.equalsIgnoreCase(origOrgAddr))                    throw ServiceException.INVALID_REQUEST(                            "Changing organizer of an appointment is not allowed: old=" + origOrgAddr + ", new=" + newOrgAddr, null);            } else {                if (originalOrganizer != null)                    throw ServiceException.INVALID_REQUEST(                            "Removing organizer of an appointment is not allowed", null);            }        }        // If modifying recurrence series (rather than an instance) and the        // start time (HH:MM:SS) is changing, we need to update the time        // component of RECURRENCE-ID in all exception instances.        boolean needRecurrenceIdUpdate = false;        ParsedDateTime oldDtStart = null;        ParsedDuration dtStartMovedBy = null;        ArrayList<Invite> toUpdate = new ArrayList<Invite>();        if (!isCancel && newInvite.isRecurrence()) {            Invite defInv = getDefaultInvite();            if (defInv != null) {                oldDtStart = defInv.getStartTime();                ParsedDateTime newDtStart = newInvite.getStartTime();                if (!newDtStart.sameTime(oldDtStart)) {                    needRecurrenceIdUpdate = true;                    dtStartMovedBy = newInvite.getStartTime().difference(oldDtStart);                }            }        }        boolean modifiedAppt = false;        Invite prev = null; // (the first) invite which has been made obsolete by the new one coming in                ArrayList<Invite> toRemove = new ArrayList<Invite>(); // Invites to remove from our blob store        ArrayList<Integer> idxsToRemove = new ArrayList<Integer>(); // indexes to remove from mInvites                for (int i = 0; i < numInvites(); i++) {            Invite cur = getInvite(i);            // UID already matches...next check if RecurId matches            // if so, then seqNo is next            // finally use DTStamp            //            // See RFC2446: 2.1.5 Message Sequencing            //            if ((cur.getRecurId() != null && cur.getRecurId().equals(newInvite.getRecurId())) ||                    (cur.getRecurId() == null && newInvite.getRecurId() == null)) {                if (force ||                        ((cur.getSeqNo() < newInvite.getSeqNo())                        // in the CANCEL case, accept one with the exact same DTSTAMP as the request                        // being cancelled ( >= instead of strictly > for DTSTAMP)                        || (                                (!isCancel && (cur.getSeqNo() == newInvite.getSeqNo() && cur.getDTStamp() < newInvite.getDTStamp()))                                 ||                                 (isCancel && (cur.getSeqNo() == newInvite.getSeqNo() && cur.getDTStamp() <= newInvite.getDTStamp())))))                 {                    Invite inf = (Invite)mInvites.get(i);                    toRemove.add(inf);                                        // add to FRONT of list, so when we iterate for the removals we go from HIGHER TO LOWER                    // that way the numbers all match up as the list contracts!                    idxsToRemove.add(0, new Integer(i));                                        // clean up any old REPLYs that have been made obscelete by this new invite                    mReplyList.removeObsceleteEntries(newInvite.getRecurId(), newInvite.getSeqNo(), newInvite.getDTStamp());                                        prev = cur;                    modifiedAppt = true;                    if (isCancel && !newInvite.hasRecurId()) {                        addNewOne = false; // can't CANCEL just the recurId=0 entry -- we must be deleting the whole series                    }                } else {                    // found a more-recent invite already here -- so don't add                    // the passed-in one!                    addNewOne = false;                }//              break; // don't stop here!  new Invite *could* obscelete multiple existing ones!             } else if (needRecurrenceIdUpdate) {                RecurId rid = cur.getRecurId();                if (rid != null && rid.getDt().sameTime(oldDtStart))                    toUpdate.add(cur);            }        }        if (addNewOne) {            newInvite.setAppointment(this);            Account account = getMailbox().getAccount();            boolean thisAcctIsOrganizer =                newInvite.thisAcctIsOrganizer(account);            if (prev!=null && !thisAcctIsOrganizer && newInvite.sentByMe()) {                // A non-organizer attendee is modifying data on his/her                // appointment.  Any information that is tracked in                // metadata rather than in the iCal MIME part must be                // carried over from the last invite to the new one.                newInvite.setPartStat(prev.getPartStat());                newInvite.setRsvp(prev.getRsvp());                newInvite.getAppointment().saveMetadata();                // No need to mark invite as modified item in mailbox as                // it has already been marked as a created item.            } else {                processPartStat(newInvite,                                pm != null ? pm.getMimeMessage() : null,                                false,                                newInvite.getPartStat());            }            mInvites.add(newInvite);                        // the appointment stores an uber-tzmap, for its uber-recurrence            // this might give us problems if we had two invites with conflicting TZ            // defs....should be very unlikely            mTzMap.add(newInvite.getTimeZoneMap());            // Adjust DTSTART of RECURRENCE-ID for exceptions if DTSTART of            // series is changing.  Notice we're only doing this when            // addNewOne is true.  We don't want to make any updates if the            // new invite ends up getting ignored due to older SEQUENCE.            if (needRecurrenceIdUpdate) {                for (Invite inv: toUpdate) {                    RecurId rid = inv.getRecurId();                    ParsedDateTime dt = rid.getDt().add(dtStartMovedBy);                    RecurId newRid = new RecurId(dt, rid.getRange());                    inv.setRecurId(newRid);                    // For CANCELLED instances, set DTSTART to the same time                    // used in RECURRENCE-ID.                    if (ICalTok.CANCEL.toString().equals(inv.getMethod()))                        inv.setDtStart(dt);                }            }            modifyBlob(toRemove, toUpdate, pm, newInvite, volumeId);            modifiedAppt = true;        } else {            modifyBlob(toRemove, toUpdate, null, null, volumeId);        }                // now remove the inviteid's from our list          for (Iterator iter = idxsToRemove.iterator(); iter.hasNext();) {            Integer i = (Integer)iter.next();            mInvites.remove(i.intValue());        }        if (getFolderId() != folderId) {        	// Move appointment to a different folder.        	Folder folder = getMailbox().getFolderById(folderId);        	move(folder);        }        boolean hasRequests = false;        for (Iterator iter = mInvites.iterator(); iter.hasNext();) {            Invite cur = (Invite)iter.next();            if (cur.getMethod().equals(ICalTok.REQUEST.toString()) ||                    cur.getMethod().equals(ICalTok.PUBLISH.toString()))             {                hasRequests = true;                break;            }        }                if (!hasRequests) {            this.delete(); // delete this appointment from the table, it                            // doesn't have anymore REQUESTs!        } else {            if (modifiedAppt) {                if (!updateRecurrence()) {                    // no default invite!  This appointment no longer valid                    this.delete();                } else {                    this.saveMetadata();                }            }        }    }