internal void OnFetch (ImapEngine engine, int index, CancellationToken cancellationToken) { var labelsChangedEventArgs = new MessageLabelsChangedEventArgs (index); var flagsChangedEventArgs = new MessageFlagsChangedEventArgs (index); var modSeqChangedEventArgs = new ModSeqChangedEventArgs (index); var token = engine.ReadToken (cancellationToken); bool modSeqChanged = false; bool labelsChanged = false; bool flagsChanged = false; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); do { token = engine.ReadToken (cancellationToken); if (token.Type == ImapTokenType.CloseParen || token.Type == ImapTokenType.Eoln) break; if (token.Type != ImapTokenType.Atom) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); var atom = (string) token.Value; ulong modseq; uint uid; switch (atom) { case "MODSEQ": token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.Atom || !ulong.TryParse ((string) token.Value, out modseq)) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (modseq > HighestModSeq) UpdateHighestModSeq (modseq); modSeqChangedEventArgs.ModSeq = modseq; labelsChangedEventArgs.ModSeq = modseq; flagsChangedEventArgs.ModSeq = modseq; modSeqChanged = true; break; case "UID": token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.Atom || !uint.TryParse ((string) token.Value, out uid) || uid == 0) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); modSeqChangedEventArgs.UniqueId = new UniqueId (UidValidity, uid); labelsChangedEventArgs.UniqueId = new UniqueId (UidValidity, uid); flagsChangedEventArgs.UniqueId = new UniqueId (UidValidity, uid); break; case "FLAGS": flagsChangedEventArgs.Flags = ImapUtils.ParseFlagsList (engine, atom, flagsChangedEventArgs.UserFlags, cancellationToken); flagsChanged = true; break; case "X-GM-LABELS": labelsChangedEventArgs.Labels = ImapUtils.ParseLabelsList (engine, cancellationToken); labelsChanged = true; break; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); } } while (true); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); if (flagsChanged) OnMessageFlagsChanged (flagsChangedEventArgs); if (labelsChanged) OnMessageLabelsChanged (labelsChangedEventArgs); if (modSeqChanged) OnModSeqChanged (modSeqChangedEventArgs); }
void FetchStream (ImapEngine engine, ImapCommand ic, int index) { var token = engine.ReadToken (ic.CancellationToken); var labels = new MessageLabelsChangedEventArgs (index); var flags = new MessageFlagsChangedEventArgs (index); var modSeq = new ModSeqChangedEventArgs (index); var ctx = (FetchStreamContext) ic.UserData; var section = new StringBuilder (); bool modSeqChanged = false; bool labelsChanged = false; bool flagsChanged = false; var buf = new byte[4096]; long nread = 0, size = 0; UniqueId? uid = null; Stream stream; int n; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); do { token = engine.ReadToken (ic.CancellationToken); if (token.Type == ImapTokenType.CloseParen || token.Type == ImapTokenType.Eoln) break; if (token.Type != ImapTokenType.Atom) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); var atom = (string) token.Value; int offset = 0, length; ulong modseq; uint value; switch (atom) { case "BODY": token = engine.ReadToken (ic.CancellationToken); if (token.Type != ImapTokenType.OpenBracket) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); section.Clear (); do { token = engine.ReadToken (ic.CancellationToken); if (token.Type == ImapTokenType.CloseBracket) break; if (token.Type == ImapTokenType.OpenParen) { section.Append (" ("); do { token = engine.ReadToken (ic.CancellationToken); if (token.Type == ImapTokenType.CloseParen) break; // the header field names will generally be atoms or qstrings but may also be literals switch (token.Type) { case ImapTokenType.Literal: section.Append (engine.ReadLiteral (ic.CancellationToken)); break; case ImapTokenType.QString: case ImapTokenType.Atom: section.Append ((string) token.Value); break; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); } section.Append (' '); } while (true); if (section[section.Length - 1] == ' ') section.Length--; section.Append (')'); } else if (token.Type != ImapTokenType.Atom) { throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); } else { section.Append ((string) token.Value); } } while (true); if (token.Type != ImapTokenType.CloseBracket) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); token = engine.ReadToken (ic.CancellationToken); if (token.Type == ImapTokenType.Atom) { // this might be a region ("<###>") var expr = (string) token.Value; if (expr.Length > 2 && expr[0] == '<' && expr[expr.Length - 1] == '>') { var region = expr.Substring (1, expr.Length - 2); int.TryParse (region, out offset); token = engine.ReadToken (ic.CancellationToken); } } switch (token.Type) { case ImapTokenType.Literal: length = (int) token.Value; size += length; stream = CreateStream (uid, section.ToString (), offset, length); try { while ((n = engine.Stream.Read (buf, 0, buf.Length, ic.CancellationToken)) > 0) { stream.Write (buf, 0, n); nread += n; ctx.Report (nread, size); } stream.Position = 0; } catch { stream.Dispose (); throw; } break; case ImapTokenType.QString: case ImapTokenType.Atom: var buffer = Encoding.UTF8.GetBytes ((string) token.Value); length = buffer.Length; nread += length; size += length; stream = CreateStream (uid, section.ToString (), offset, length); try { stream.Write (buffer, 0, length); ctx.Report (nread, size); stream.Position = 0; } catch { stream.Dispose (); throw; } break; case ImapTokenType.Nil: stream = CreateStream (uid, section.ToString (), offset, 0); break; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); } if (uid.HasValue) ctx.Sections[section.ToString ()] = CommitStream (stream, uid.Value); else ctx.Sections[section.ToString ()] = stream; break; case "UID": token = engine.ReadToken (ic.CancellationToken); if (token.Type != ImapTokenType.Atom || !uint.TryParse ((string) token.Value, out value) || value == 0) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); uid = new UniqueId (UidValidity, value); foreach (var key in ctx.Sections.Keys.ToArray ()) ctx.Sections[key] = CommitStream (ctx.Sections[key], uid.Value); labels.UniqueId = uid.Value; flags.UniqueId = uid.Value; break; case "MODSEQ": token = engine.ReadToken (ic.CancellationToken); if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); token = engine.ReadToken (ic.CancellationToken); if (token.Type != ImapTokenType.Atom || !ulong.TryParse ((string) token.Value, out modseq)) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); token = engine.ReadToken (ic.CancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (modseq > HighestModSeq) UpdateHighestModSeq (modseq); modSeq.ModSeq = modseq; labels.ModSeq = modseq; flags.ModSeq = modseq; break; case "FLAGS": // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message flags. flags.Flags = ImapUtils.ParseFlagsList (engine, atom, flags.UserFlags, ic.CancellationToken); flagsChanged = true; break; case "X-GM-LABELS": // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message labels. labels.Labels = ImapUtils.ParseLabelsList (engine, ic.CancellationToken); labelsChanged = true; break; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); } } while (true); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); if (flagsChanged) OnMessageFlagsChanged (flags); if (labelsChanged) OnMessageLabelsChanged (labels); if (modSeqChanged) OnModSeqChanged (modSeq); }