// ============================================================================= // Mailbox Export // ============================================================================= /// <summary> /// Export mail from a single mailbox. /// </summary> /// <param name="mbox"> /// A reference to the mailbox. /// </param> /// <param name="quoted"> /// If <c>true</c> export using "From" quoting instead of "Content-Length" headers. /// </param> /// <param name="uids"> /// A list of uids to be exported or <c>null</c> /// </param> /// <param name="mailTotal"> /// Can specify a total number of mails when multiple mailboxes are going to be /// exported. /// </param> /// <param name="mailCounter"> /// Incremented for each exported mail. /// </param> /// <returns> /// <c>true</c> on success. /// </returns> public bool MailboxExport(MBoxRef mbox, bool quoted, uint[] uids, uint mailTotal, ref uint mailCounter) { if(!mbox.IsValid) return false; uint ucnt = (uids == null) ? mbox.Messages : (uint)uids.Length; if(ucnt > mailTotal) mailTotal = ucnt; string mailbox = mbox.Name; // get rid of INBOX and prefix personal mailboxes with the account name uint rnsi; string fullname = server.FriendlyName(mailbox, out rnsi); string prefix = server[rnsi].Prefix; if(prefix != "" && fullname.StartsWith(prefix)) // strip the ns prefix fullname = fullname.Substring(prefix.Length); // create the output file if (!application.ExportMailbox(fullname, rnsi)) return false; if(ucnt == 0) { MonitorInfo("Mailbox exported: {0} [no mails]", fullname.PadRight(32)); return true; } // open the IMAP mailbox if(data.Current.Name != mailbox) { ZIMapCommand.Examine exa = new ZIMapCommand.Examine(factory); exa.Queue(mailbox); if(!exa.CheckSuccess(":Cannot open Mailbox: " + mailbox)) return false; } // loop over mail items ZIMapCommand.Fetch.Item item = new ZIMapCommand.Fetch.Item(); byte[] head = null; byte[] body = null; ZIMapCommand.Generic current = null; ZIMapFactory.Bulk bulk = factory.CreateBulk("FETCH", 4, uids != null); ZIMapMessage msg = new ZIMapMessage(); uint ucur = 0; // current item/UID uint uerr = 0; // error counter uint urun = 0; // mail counter uint urdy = 0; string key = (uids == null) ? "item" : "UID"; while(urdy < ucnt) { // step 1: queue request and check for response ... bool done = bulk.NextCommand(ref current); ZIMapCommand.Fetch cmd = (ZIMapCommand.Fetch)current; // step 2: check server reply for error ... if(done) { ucur = (uids == null) ? ++urdy : uids[urdy++]; if (cmd.Items == null || cmd.Items.Length != 1) { MonitorError(!cmd.CheckSuccess() ? "Cannot fetch mail ({0}={1}): {2}" : "Mail not existing ({0}={1})", key, ucur, cmd.Result.Message); done = false; uerr++; } } // step 3: process data sent by server ... if(done) { item = cmd.Items[0]; head = item.Literal(0); body = item.Literal(1); if (head == null) { MonitorError("Mail data incomplete ({0}={1})", key, ucur); done = false; uerr++; } if(body == null) body = new byte[0]; // message without body } if(done) { msg.Parse(head, true); // use the server's INTERNALDATE if we got it... DateTime date; string sdat; if (ZIMapFactory.FindInParts(item.Parts, "INTERNALDATE", out sdat)) date = ZIMapConverter.DecodeIMapTime(sdat, true); else date = msg.DateBinary; // finally write mail data to file ... string flags = (item.Flags == null) ? null : string.Join(" ", item.Flags); if (!application.Export.WriteMail(msg.From, date, flags, head, body, quoted)) { MonitorError("Mail could not be written ({0}={1})", key, ucur); uerr++; } progress.Update(mailCounter++, mailTotal); } // step 4: create a new request if(urun < ucnt) { ucur = (uids == null) ? ++urun : uids[urun++]; cmd.Reset(); cmd.Queue(ucur, "FLAGS INTERNALDATE BODY.PEEK[HEADER] BODY.PEEK[TEXT]"); } } bulk.Dispose(); MonitorInfo("Mailbox exported: {0} [{1,4} mails]", fullname.PadRight(32), urun); return (uerr == 0); }
/// <summary> /// Get information about mailboxes and subfolders. /// </summary> /// <param name="qualifier"> /// Can be a prefix like "user" or "MyFolder.Something" that gets prepended /// to the filter argument. There is no need to put a hierarchy delimiter /// at the end, this function automatically inserts it to separate qualifier /// and filter. /// </param> /// <param name="filter"> /// Can be a folder name optionally containing a '*' or a '%' character. /// </param> /// <param name="subscribed"> /// A number indicating if the subscription status should by loaded (for /// <c>subscribed != 0</c>, if the status should be loaded (<c>1</c>) or /// if only subscribed mailboxes should be returned (<c>2</c>). /// </param> /// <param name="detailed"> /// When <c>false</c> only the mailbox name and the subscription status are /// returned (which is quite fast). A value of <c>true</c> also gets the /// message, seen and recent counts for each mailbox. /// </param> /// <returns> /// On success an array of Mailbox info structures or <c>null</c> on error. /// </returns> public MailBox[] Mailboxes(string qualifier, string filter, uint subscribed, bool detailed) { if(factory == null) return null; ZIMapCommand.List cmdList = (ZIMapCommand.List)factory.CreateByName((subscribed == 2) ? "LSUB" : "LIST"); if(cmdList == null) return null; progress.Update(0); ZIMapCommand.Lsub cmdLSub = null; if(subscribed == 1) cmdLSub = new ZIMapCommand.Lsub(factory); // does server specific things ... Server.NormalizeQualifierFilter(ref qualifier, ref filter); // send the commands ... cmdList.Queue(qualifier, filter); if(cmdLSub != null) cmdLSub.Queue(qualifier, filter); progress.Update(5); // wait for the mailbox list ... ZIMapCommand.List.Item[] items = cmdList.Items; cmdList.Dispose(); if(items == null) { MonitorError("Mailboxes: got no mailboxes"); return null; } progress.Update(15); // create mailbox data ... MailBox[] mbox = new MailBox[items.Length]; int irun = 0; foreach(ZIMapCommand.List.Item i in items) { mbox[irun].Name = i.Name; mbox[irun].Delimiter = i.Delimiter; mbox[irun].Attributes = i.Attributes; mbox[irun].Subscribed = (subscribed == 2); if(detailed) { ZIMapCommand.Examine cmd = new ZIMapCommand.Examine(factory); cmd.UserData = irun; cmd.Queue(i.Name); } irun++; } // get subscription info ... if(cmdLSub != null) { items = cmdLSub.Items; progress.Update(20); if(!cmdLSub.CheckSuccess()) MonitorError("Mailboxes: got no subscription info"); else { for(uint usub=0; usub < mbox.Length; usub++) mbox[usub].HasSubscription = true; if(items != null) { int icur = 0; foreach(ZIMapCommand.List.Item i in items) { for(int imax=irun; imax > 0; imax--) { if(icur >= irun) icur = 0; if(i.Name == mbox[icur].Name) { mbox[icur++].Subscribed = true; break; } icur++; } } } } cmdLSub.Dispose(); } // fetch details ... if(!detailed) { progress.Done(); return mbox; } items = null; MonitorInfo("Mailboxes: Fetching " + irun + " details"); progress.Update(25); // TODO: ZIMapApplication - The Mailboxes() code needs rewrite using bulk factory.ExecuteCommands(true); ZIMapCommand[] cmds = factory.CompletedCommands; progress.Push(30, 100); foreach(ZIMapCommand c in cmds) { ZIMapCommand.Examine cmd = c as ZIMapCommand.Examine; if(cmd == null) continue; irun = (int)(cmd.UserData); mbox[irun].HasDetails = cmd.CheckSuccess(); mbox[irun].Messages = cmd.Messages; mbox[irun].Recent = cmd.Recent; mbox[irun].Unseen = cmd.Unseen; mbox[irun].Flags = cmd.Flags; progress.Update((uint)irun, (uint)mbox.Length); } progress.Pop(); factory.DisposeCommands(null, false); progress.Done(); return mbox; }