internal cStoreFeedback(IEnumerable <iMessageHandle> pMessageHandles, eStoreOperation pOperation, cStorableFlags pFlags) { if (pMessageHandles == null) { throw new ArgumentNullException(nameof(pMessageHandles)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } object lMessageCache = null; foreach (var lMessageHandle in pMessageHandles) { if (lMessageHandle == null) { throw new ArgumentOutOfRangeException(nameof(pMessageHandles), "contains nulls"); } if (lMessageCache == null) { lMessageCache = lMessageHandle.MessageCache; } else if (!ReferenceEquals(lMessageHandle.MessageCache, lMessageCache)) { throw new ArgumentOutOfRangeException(nameof(pMessageHandles), "contains mixed message caches"); } } mItems = new List <cStoreFeedbackItem>(from lMessageHandle in pMessageHandles.Distinct() select new cStoreFeedbackItem(lMessageHandle)); mOperation = pOperation; mFlags = pFlags; }
internal cUIDStoreFeedback(IEnumerable <cUID> pUIDs, eStoreOperation pOperation, cStorableFlags pFlags) { if (pUIDs == null) { throw new ArgumentNullException(nameof(pUIDs)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } uint lUIDValidity = 0; foreach (var lUID in pUIDs) { if (lUID == null) { throw new ArgumentOutOfRangeException(nameof(pUIDs), "contains nulls"); } if (lUIDValidity == 0) { lUIDValidity = lUID.UIDValidity; } else if (lUID.UIDValidity != lUIDValidity) { throw new ArgumentOutOfRangeException(nameof(pUIDs), "contains mixed uidvalidities"); } } mItems = new List <cUIDStoreFeedbackItem>(from lUID in pUIDs.Distinct() select new cUIDStoreFeedbackItem(lUID)); mOperation = pOperation; mFlags = pFlags; }
private bool ZTryParseFlagNames(string pText, out cStorableFlags rFlags) { if (pText == null) { rFlags = cStorableFlags.Empty; return(true); } List <string> lFlags = new List <string>(); foreach (var lFlag in pText.Trim().Split(' ')) { if (!string.IsNullOrWhiteSpace(lFlag)) { lFlags.Add(lFlag); } } if (lFlags.Count == 0) { rFlags = cStorableFlags.Empty; return(true); } try { rFlags = new cStorableFlags(lFlags); } catch { rFlags = null; return(false); } return(true); }
private string ZFlagNames(cStorableFlags pFlags) { if (pFlags == null || pFlags.Count == 0) { return(string.Empty); } StringBuilder lBuilder = new StringBuilder(); bool lFirst = true; foreach (var lFlag in pFlags) { if (lFirst) { lFirst = false; } else { lBuilder.Append(" "); } lBuilder.Append(lFlag); } return(lBuilder.ToString()); }
internal static void _Tests(cTrace.cContext pParentContext) { bool lFailed; var lFlags = new cStorableFlagList(); lFlags.Add("a"); lFlags.Add("b"); lFlags.Add(kMessageFlag.Answered, kMessageFlag.Deleted); lFlags.Add(kMessageFlag.Answered, kMessageFlag.Deleted, kMessageFlag.Forwarded); lFlags.Add(@"\answereD"); lFlags.Add(@"\ansWereD", "A", @"\deleteD"); lFailed = false; try { lFlags.Add("fr ed"); } catch { lFailed = true; } if (!lFailed) { throw new cTestsException($"{nameof(cStorableFlagList)}.1"); } lFailed = false; try { lFlags.Add(kMessageFlag.Answered, kMessageFlag.Deleted, kMessageFlag.Draft, kMessageFlag.Recent); } catch { lFailed = true; } if (!lFailed) { throw new cTestsException($"{nameof(cStorableFlagList)}.1"); } if (lFlags.Count != 5) { throw new cTestsException($"{nameof(cStorableFlagList)}.2"); } if (!lFlags.Contains("A") || !lFlags.Contains("B") || !lFlags.Contains(@"\aNswereD") || lFlags.Contains(kMessageFlag.Draft) || !lFlags.Contains("$forwarded")) { throw new cTestsException($"{nameof(cStorableFlagList)}.3"); } cStorableFlags lF1 = new cStorableFlags("a", "A", "b", @"\answered", "\\deleted", kMessageFlag.Forwarded); cFetchableFlags lF2 = new cFetchableFlags("a", "A", "b", @"\answered", "\\deleted", kMessageFlag.Recent); cStorableFlags lF3 = new cStorableFlags("a", "b", "\\deleted", kMessageFlag.Forwarded); if (!lFlags.Contains(lF1) || lFlags.Contains(lF2) || !lFlags.Contains(lF3)) { throw new cTestsException($"{nameof(cStorableFlagList)}.4"); } lFlags.Remove("A"); if (lFlags.Count != 4 || lFlags.Contains(lF1) || lFlags.Contains(lF3)) { throw new cTestsException($"{nameof(cStorableFlagList)}.5"); } lFlags.Remove("B", "$forwarded", @"\answered"); if (lFlags.Count != 1 || lFlags.Contains(lF3)) { throw new cTestsException($"{nameof(cStorableFlagList)}.6"); } }
internal cUIDStoreFeedback(cUID pUID, eStoreOperation pOperation, cStorableFlags pFlags) { if (pUID == null) { throw new ArgumentNullException(nameof(pUID)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } mItems = new List <cUIDStoreFeedbackItem>(); mItems.Add(new cUIDStoreFeedbackItem(pUID)); mOperation = pOperation; mFlags = pFlags; }
internal cStoreFeedback(iMessageHandle pMessageHandle, eStoreOperation pOperation, cStorableFlags pFlags) { if (pMessageHandle == null) { throw new ArgumentNullException(nameof(pMessageHandle)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } mItems = new List <cStoreFeedbackItem>(); mItems.Add(new cStoreFeedbackItem(pMessageHandle)); mOperation = pOperation; mFlags = pFlags; }
internal void IncrementSummary(iMessageHandle pMessageHandle, eStoreOperation pOperation, cStorableFlags pFlags, ref sStoreFeedbackSummary pSummary) { if (WasNotUnchangedSince) { pSummary.WasNotUnchangedSinceCount++; return; } if (ReceivedFlagsUpdate) { pSummary.UpdatedCount++; return; } if (pMessageHandle == null) { pSummary.UnknownCount++; return; } if (pMessageHandle.Expunged) { pSummary.ExpungedCount++; return; } if (pMessageHandle.Flags == null) { pSummary.UnknownCount++; return; } switch (pOperation) { case eStoreOperation.add: if (pMessageHandle.Flags.Contains(pFlags)) { pSummary.ReflectsOperationCount++; } else { pSummary.NotReflectsOperationCount++; } return; case eStoreOperation.remove: if (pMessageHandle.Flags.Contains(pFlags)) { pSummary.NotReflectsOperationCount++; } else { pSummary.ReflectsOperationCount++; } return; case eStoreOperation.replace: if (pMessageHandle.Flags.SymmetricDifference(pFlags, kMessageFlag.Recent).Count() == 0) { pSummary.ReflectsOperationCount++; } else { pSummary.NotReflectsOperationCount++; } return; } pSummary.UnknownCount++; // shouldn't happen }
/// <summary> /// Stores flags for a set of messages. The mailbox that the messages are in must be selected. /// </summary> /// <param name="pMessages"></param> /// <param name="pOperation">The type of store operation.</param> /// <param name="pFlags"></param> /// <param name="pIfUnchangedSinceModSeq"></param> /// <returns>Feedback on the success (or otherwise) of the store.</returns> /// <remarks> /// <paramref name="pIfUnchangedSinceModSeq"/> may only be specified if the containing mailbox's <see cref="cMailbox.HighestModSeq"/> is not zero. /// (i.e. <see cref="cCapabilities.CondStore"/> is in use and the mailbox supports the persistent storage of mod-sequences.) /// If a message has been modified since the specified value then the server will fail the store for that message. /// </remarks> public cStoreFeedback Store(IEnumerable <cMessage> pMessages, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq = null) { var lContext = mRootContext.NewMethodV(nameof(cIMAPClient), nameof(Store), 3); var lFeedback = new cStoreFeedback(pMessages, pOperation, pFlags); var lTask = ZStoreAsync(lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext); mSynchroniser.Wait(lTask, lContext); return(lFeedback); }
internal async Task <cUIDStoreFeedback> UIDStoreAsync(iMailboxHandle pMailboxHandle, IEnumerable <cUID> pUIDs, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq) { var lContext = mRootContext.NewMethod(nameof(cIMAPClient), nameof(UIDStore)); var lFeedback = new cUIDStoreFeedback(pUIDs, pOperation, pFlags); await ZUIDStoreAsync(pMailboxHandle, lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext).ConfigureAwait(false); return(lFeedback); }
private async Task ZUIDStoreAsync(iMailboxHandle pMailboxHandle, cUIDStoreFeedback pFeedback, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq, cTrace.cContext pParentContext) { var lContext = pParentContext.NewMethod(nameof(cIMAPClient), nameof(ZUIDStoreAsync), pFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq); if (mDisposed) { throw new ObjectDisposedException(nameof(cIMAPClient)); } var lSession = mSession; if (lSession == null || lSession.SelectedMailboxDetails?.SelectedForUpdate != true) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.NotSelectedForUpdate); } if (pMailboxHandle == null) { throw new ArgumentNullException(nameof(pMailboxHandle)); } if (pFeedback == null) { throw new ArgumentNullException(nameof(pFeedback)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } if (pIfUnchangedSinceModSeq == 0) { throw new ArgumentOutOfRangeException(nameof(pIfUnchangedSinceModSeq)); } if (pIfUnchangedSinceModSeq != null && !lSession.Capabilities.CondStore) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.CondStoreNotInUse); } if (pFeedback.Count == 0) { return; } // it is valid to add or remove zero flags according to the ABNF (!) using (var lToken = mCancellationManager.GetToken(lContext)) { var lMC = new cMethodControl(mTimeout, lToken.CancellationToken); await lSession.UIDStoreAsync(lMC, pMailboxHandle, pFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext).ConfigureAwait(false); } }
private async Task ZStoreAsync(cMethodControl pMC, cStoreFeedback pFeedback, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq, cTrace.cContext pParentContext) { var lContext = pParentContext.NewMethod(nameof(cSession), nameof(ZStoreAsync), pMC, pFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq); // no validation ... all the parameters have been validated already by the cSession by the time we get here using (var lBuilder = new cCommandDetailsBuilder()) { lBuilder.Add(await mSelectExclusiveAccess.GetBlockAsync(pMC, lContext).ConfigureAwait(false)); // block select cSelectedMailbox lSelectedMailbox = mMailboxCache.CheckInSelectedMailbox(pFeedback); if (!lSelectedMailbox.SelectedForUpdate) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.NotSelectedForUpdate); } lBuilder.Add(await mPipeline.GetIdleBlockTokenAsync(pMC, lContext).ConfigureAwait(false)); // stop the pipeline from iding (idle is msnunsafe) lBuilder.Add(await mMSNUnsafeBlock.GetTokenAsync(pMC, lContext).ConfigureAwait(false)); // wait until all commands that are msnunsafe complete, block all commands that are msnunsafe // uidvalidity must be captured before the handles are resolved lBuilder.AddUIDValidity(lSelectedMailbox.MessageCache.UIDValidity); // collector cStoreFeedbackCollector lFeedbackCollector = new cStoreFeedbackCollector(); // resolve the handles to MSNs foreach (var lItem in pFeedback) { var lMSN = lSelectedMailbox.GetMSN(lItem.MessageHandle); if (lMSN != 0) { lFeedbackCollector.Add(lMSN, lItem); } } // if no handles were resolved, we are done if (lFeedbackCollector.Count == 0) { return; } // build the command lBuilder.Add(kStoreCommandPartStoreSpace, new cTextCommandPart(cSequenceSet.FromUInts(lFeedbackCollector.UInts)), cCommandPart.Space); if (pIfUnchangedSinceModSeq != null) { lBuilder.Add(kStoreCommandPartLParenUnchangedSinceSpace, new cTextCommandPart(pIfUnchangedSinceModSeq.Value), kStoreCommandPartRParenSpace); } lBuilder.Add(pOperation, pFlags); // add the hook var lHook = new cCommandHookStore(lFeedbackCollector, null, lSelectedMailbox); lBuilder.Add(lHook); // submit the command var lResult = await mPipeline.ExecuteAsync(pMC, lBuilder.EmitCommandDetails(), lContext).ConfigureAwait(false); // NOTE: updates may have been done on both OK and NO responses (see rfc 2180/ 7162) so we can't throw on a NO for this command ... NO indicates that some (or all) of the messages have pending deletes // throw on a bad if (lResult.ResultType == eCommandResultType.bad) { fCapabilities lTryIgnoring; if (pIfUnchangedSinceModSeq == null) { lTryIgnoring = 0; } else { lTryIgnoring = fCapabilities.condstore; } throw new cProtocolErrorException(lResult, lTryIgnoring, lContext); } if (lResult.ResultType == eCommandResultType.ok) { lContext.TraceInformation("store success"); } else { lContext.TraceInformation("store possible partial success"); } } }
internal cUIDStoreFeedback UIDStore(iMailboxHandle pMailboxHandle, IEnumerable <cUID> pUIDs, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq) { var lContext = mRootContext.NewMethod(nameof(cIMAPClient), nameof(UIDStore)); var lFeedback = new cUIDStoreFeedback(pUIDs, pOperation, pFlags); var lTask = ZUIDStoreAsync(pMailboxHandle, lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext); mSynchroniser.Wait(lTask, lContext); return(lFeedback); }
/// <summary> /// Asynchronously stores flags for a set of messages. The mailbox that the messages are in must be selected. /// </summary> /// <param name="pMessages"></param> /// <param name="pOperation">The type of store operation.</param> /// <param name="pFlags"></param> /// <param name="pIfUnchangedSinceModSeq"></param> /// <inheritdoc cref="Store(IEnumerable{cMessage}, eStoreOperation, cStorableFlags, ulong?)" select="returns|remarks"/> public async Task <cStoreFeedback> StoreAsync(IEnumerable <cMessage> pMessages, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq = null) { var lContext = mRootContext.NewMethodV(nameof(cIMAPClient), nameof(StoreAsync), 3); var lFeedback = new cStoreFeedback(pMessages, pOperation, pFlags); await ZStoreAsync(lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext).ConfigureAwait(false); return(lFeedback); }
internal async Task <cStoreFeedback> StoreAsync(iMessageHandle pMessageHandle, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq) { var lContext = mRootContext.NewMethodV(nameof(cIMAPClient), nameof(StoreAsync), 1); var lFeedback = new cStoreFeedback(pMessageHandle, pOperation, pFlags); await ZStoreAsync(lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext).ConfigureAwait(false); return(lFeedback); }
public async Task UIDStoreAsync(cMethodControl pMC, iMailboxHandle pMailboxHandle, cUIDStoreFeedback pFeedback, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq, cTrace.cContext pParentContext) { var lContext = pParentContext.NewMethod(nameof(cSession), nameof(UIDStoreAsync), pMC, pMailboxHandle, pFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq); if (mDisposed) { throw new ObjectDisposedException(nameof(cSession)); } if (_ConnectionState != eConnectionState.selected) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.NotSelected); } if (pMailboxHandle == null) { throw new ArgumentNullException(nameof(pMailboxHandle)); } if (pFeedback == null) { throw new ArgumentNullException(nameof(pFeedback)); } if (pFlags == null) { throw new ArgumentNullException(nameof(pFlags)); } if (pFeedback.Count == 0) { throw new ArgumentOutOfRangeException(nameof(pFeedback)); } if (pIfUnchangedSinceModSeq == 0) { throw new ArgumentOutOfRangeException(nameof(pIfUnchangedSinceModSeq)); } if (pIfUnchangedSinceModSeq != null && !_Capabilities.CondStore) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.CondStoreNotInUse); } uint lUIDValidity = pFeedback[0].UID.UIDValidity; cSelectedMailbox lSelectedMailbox = mMailboxCache.CheckIsSelectedMailbox(pMailboxHandle, lUIDValidity); // to be repeated inside the select lock if (!lSelectedMailbox.SelectedForUpdate) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.NotSelectedForUpdate); // to be repeated inside the select lock } cStoreFeedbackCollector lFeedbackCollector = new cStoreFeedbackCollector(pFeedback); await ZUIDStoreAsync(pMC, pMailboxHandle, lUIDValidity, lFeedbackCollector, pOperation, pFlags, pIfUnchangedSinceModSeq, pFeedback, lContext).ConfigureAwait(false); }
internal cStoreFeedback Store(iMessageHandle pMessageHandle, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq) { var lContext = mRootContext.NewMethodV(nameof(cIMAPClient), nameof(Store), 1); var lFeedback = new cStoreFeedback(pMessageHandle, pOperation, pFlags); var lTask = ZStoreAsync(lFeedback, pOperation, pFlags, pIfUnchangedSinceModSeq, lContext); mSynchroniser.Wait(lTask, lContext); return(lFeedback); }
private async Task ZUIDStoreAsync(cMethodControl pMC, iMailboxHandle pMailboxHandle, uint pUIDValidity, cStoreFeedbackCollector pFeedbackCollector, eStoreOperation pOperation, cStorableFlags pFlags, ulong?pIfUnchangedSinceModSeq, cUIDStoreFeedback pFeedback, cTrace.cContext pParentContext) { var lContext = pParentContext.NewMethod(nameof(cSession), nameof(ZStoreAsync), pMC, pMailboxHandle, pUIDValidity, pFeedbackCollector, pOperation, pFlags, pIfUnchangedSinceModSeq, pFeedback); // no validation ... all the parameters have been validated already by the cSession by the time we get here using (var lBuilder = new cCommandDetailsBuilder()) { lBuilder.Add(await mSelectExclusiveAccess.GetBlockAsync(pMC, lContext).ConfigureAwait(false)); // block select cSelectedMailbox lSelectedMailbox = mMailboxCache.CheckIsSelectedMailbox(pMailboxHandle, pUIDValidity); if (!lSelectedMailbox.SelectedForUpdate) { throw new InvalidOperationException(kInvalidOperationExceptionMessage.NotSelectedForUpdate); } lBuilder.Add(await mMSNUnsafeBlock.GetBlockAsync(pMC, lContext).ConfigureAwait(false)); // this command is msnunsafe lBuilder.AddUIDValidity(pUIDValidity); // the command is sensitive to UIDValidity changes // build the command lBuilder.Add(kUIDStoreCommandPartUIDStoreSpace, new cTextCommandPart(cSequenceSet.FromUInts(pFeedbackCollector.UInts)), cCommandPart.Space); if (pIfUnchangedSinceModSeq != null) { lBuilder.Add(kStoreCommandPartLParenUnchangedSinceSpace, new cTextCommandPart(pIfUnchangedSinceModSeq.Value), kStoreCommandPartRParenSpace); } lBuilder.Add(pOperation, pFlags); // add the hook var lHook = new cCommandHookStore(pFeedbackCollector, pFeedback, lSelectedMailbox); lBuilder.Add(lHook); // submit the command var lResult = await mPipeline.ExecuteAsync(pMC, lBuilder.EmitCommandDetails(), lContext).ConfigureAwait(false); // note: the base spec states that non-existent UIDs are ignored without comment => a NO from a UID STORE is unexpected if (lResult.ResultType == eCommandResultType.ok) { lContext.TraceInformation("uid store success"); return; } fCapabilities lTryIgnoring; if (pIfUnchangedSinceModSeq == null) { lTryIgnoring = 0; } else { lTryIgnoring = fCapabilities.condstore; } if (lResult.ResultType == eCommandResultType.no) { throw new cUnsuccessfulCompletionException(lResult.ResponseText, lTryIgnoring, lContext); } throw new cProtocolErrorException(lResult, lTryIgnoring, lContext); } }