public async Task Start(StatusCb status, DataCb data, ProgressCb progress, bool compression=false) { try { status("Connecting..."); if (await connect.Connect() != ConnectStatus.Ok) throw new ConnectionFailedException(); ImapCmd cmd = null; if (await connect.ConnectTls() == ConnectStatus.Failed) { cmd = CreateImap(connect.stream); if (await cmd.Run(new StarttlsCommand()) != ReturnCode.Ok) throw new SslFailedException(); } cmd = CreateImap(connect.stream); // initial Capability await cmd.Run(new NullCommand()); // login to the server if (await cmd.Run(new LoginCommand(user, pswd)) != ReturnCode.Ok) throw new FailedLoginException(); status("Logged in to the email server"); // check if compression is supported if (compression && await cmd.Run(new CapabilityCommand(new String[] { "compress=deflate" })) == ReturnCode.Ok && await cmd.Run(new DeflateCommand()) == ReturnCode.Ok) { StreamReader reader = new StreamReader(new DeflateStream(connect.stream, CompressionMode.Decompress, true)); cmd = new ImapCmd(token, reader, connect.stream, true); status("Compression enabled"); } // get the list of mailboxes status("Fetching mailbox list..."); mailboxes = await GetMailboxesList(cmd,status,progress); status("Downloading ..."); progress(0); int processed = 0; Regex rx_msgid = new Regex("^message-id: ([^ \r\n]+)", RegexOptions.IgnoreCase | RegexOptions.Multiline); Func<String, String> getMsgId = (String message) => { int len = message.Length > 5000 ? 5000 : message.Length; Match m = rx_msgid.Match(message, 0, len); return (m.Success ? m.Groups[1].Value : ""); }; foreach (Mailbox mailbox in mailboxes) { if (token.IsCancellationRequested) return; status("{0} - {1} messages: ", mailbox.name, mailbox.cnt); if (mailbox.start < mailbox.cnt) { if (await cmd.Run(new SelectCommand(mailbox.name)) != ReturnCode.Ok) throw new FailedException(); if (!GmailSpecial(mailbox.name)) { if (await cmd.Run(new FetchCommand(FetchCommand.Fetch.Body, async delegate (String message, String msgn) { mailbox.start = int.Parse(msgn); String msgid = ""; if (host == "imap.gmail.com") { msgid = getMsgId(message); messageid[msgid] = ""; } progress((100.0 * (processed + int.Parse(msgn))) / messagesInAccount); await data(mailbox.name, message, msgid, msgn); }, mailbox.start)) != ReturnCode.Ok) { status(token.IsCancellationRequested ? "Download cancelled" : "No messages downloaded"); } } else { List<String> unique = new List<string>(); progress(0); if (await cmd.Run(new FetchCommand(FetchCommand.Fetch.MessageID, async delegate (String message, String msgn) { mailbox.start = int.Parse(msgn); String msgid = getMsgId(message); if (msgid != "" && messageid.ContainsKey(msgid)) return; else { messageid[msgid] = ""; unique.Add(msgn); } progress(100.0 * int.Parse(msgn) / mailbox.cnt); await Task.Yield(); })) != ReturnCode.Ok) { progress(0); status(token.IsCancellationRequested ? "Download cancelled" : "No messages downloaded, failed to get the list of unique messages"); } else { progress(0); status("Downloading {0} out of {1}", unique.Count, mailbox.cnt); status("{0} - {1} messages: ", mailbox.name, unique.Count); int downloaded = 0; foreach (String n in unique) { int num = int.Parse(n); if (await cmd.Run(new FetchCommand(FetchCommand.Fetch.Body, async delegate (String message, String msgn) { progress((100.0 * (processed + int.Parse(msgn))) / messagesInAccount); await data(mailbox.name, message, getMsgId(message), msgn); downloaded++; }, num, num)) != ReturnCode.Ok) { status("failed to download {0}", n); continue; } } if (token.IsCancellationRequested) status("Download cancelled"); if (unique.Count != downloaded) status("{0} out of {1} failed to download", unique.Count-downloaded, unique.Count); } } } processed += mailbox.cnt; mailbox.start = mailbox.cnt; } status("Download complete"); } finally { connect.Close(); } }
async Task<List<Mailbox>> GetMailboxesList(ImapCmd cmd, StatusCb status, ProgressCb progress) { List<Mailbox> mailboxes = new List<Mailbox>(); if (await cmd.Run(new ListCommand(async delegate (String mailbox, String ctx) { await Task.Yield(); mailboxes.Add(new Mailbox(mailbox)); })) != ReturnCode.Ok) throw new FailedException(); mailboxes.Sort((Mailbox m1, Mailbox m2) => { bool g1 = GmailSpecial(m1.name); bool g2 = GmailSpecial(m2.name); if (g1 && g2) return String.Compare(m2.name, m1.name); // reverse else if (g1) return 1; else if (g2) return -1; else return String.Compare(m1.name, m2.name); }); for (int i = 0; i < mailboxes.Count; i++) { await cmd.Run(new StatusCommand(mailboxes[i].name, async delegate (String cnt, String ctx) { await Task.Yield(); status("{0} - {1}", mailboxes[i].name, cnt); mailboxes[i].cnt = int.Parse(cnt); messagesInAccount += int.Parse(cnt); })); } status("Total messages: {0}", messagesInAccount); Dictionary<String, int> downloaded = await CheckResume(status, progress); foreach (String mbox in downloaded.Keys) { mailboxes.ForEach((Mailbox m) => { if (m.name == mbox) m.start = downloaded[mbox] > 0 ? downloaded[mbox] : 1; }); } return mailboxes; }