
/* TODO: if stream is not local, don't fetch all parts "
         add display a button for each called "Open Attachment"
   
   plain text messages should use fixed width font
   unfocus should keep previously selected messages 
*/

/*  MailBox.c: a user's view of a MailBox
 *
 *  This file is part of Aileron, a WINGs email client.
 *
 *  Copyright (c) 1999 - 2001  Nwanua Elumeze
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */



#include "Aileron.h"


typedef struct {
  WMWindow   *window;

  WMLabel     *statusl;
  void        *timestamp;

  WMSplitView *split;
  WMList      *summaryView;
  WMText      *messageView;
  WMButton    *upb, *downb, *composeb, *deleteb;

  char        *name;

  MAILSTREAM  *stream;
  WMHandlerID *timer;
  WMArray     *currentAttachments;

  unsigned long nmsgs, deleted, size, dsize, numFocused;

  unsigned short saved:1;
  unsigned short showSizes:1;
  unsigned short showDeleted:1;
  unsigned short monthFirst:1;
  unsigned short headerLevel:2;
  unsigned short sortBy:3;
  unsigned short showAll:1;
  unsigned short isFocused:1;
  unsigned short statusMapped:1;
  unsigned short reverseSortOrder:1;
  unsigned short requestClose:1;
  unsigned short RESERVED:2;

} MailBox;


static WMTextDelegate textDelegate = {
  NULL,
  (void *)OpenFile/* didDoubleClickOnPixmap */
};

static void
releaseArrayItems(void *data)
{
  if(data)
    wfree(data);
}

static void
splitViewConstrainCallback(WMSplitView *sPtr,
  int divIndex, int *min, int *max)
{
  *min = 1;
  *max = WMWidgetHeight(sPtr) - 1;
}


static void
sizeChanged(void *self, WMNotification *notif)
{
  MailBox *mbox = (MailBox *)self;
  void *object = WMGetNotificationObject(notif);
  Bool mapped;

  if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification
    && object == WMWidgetView(mbox->window)) {
    WMResizeWidget(mbox->split, WMWidgetWidth(mbox->window)-16,
       WMWidgetHeight(mbox->window)-91);
  }

  mapped = WMWidgetIsMapped(mbox->summaryView);
  if(WMWidgetHeight(mbox->summaryView) <= 5) {
    if(mapped)
      WMUnmapWidget(mbox->summaryView);
  } else {
    if(!mapped)
      WMMapWidget(mbox->summaryView);
  }

  mapped = WMWidgetIsMapped(mbox->messageView);
  if(WMWidgetHeight(mbox->messageView) <= 5) {
    if(mapped)
      WMUnmapWidget(mbox->messageView);
  } else {
    if(!mapped)
      WMMapWidget(mbox->messageView);
  }

}


static int
canhold(char *text, int targetLength)
{
  int i=0, len = strlen(text);
  while(++i < len) {
    if (WMWidthOfString(Aileron.listfont, text, i) >targetLength)
    break;
  }
  return i;
}


void clearMessageView(WMText *tPtr)
{
  WMSetTextBackgroundColor(tPtr, Aileron.whiteColor);
  WMAppendTextStream(tPtr, NULL);
}



void
sizeToString(unsigned long size, int length, char *msg)
{
  if (size >= 1000000000)
    snprintf(msg, length, "%.1fGB", size/1000000000.0);
  else if (size >= 1000000)
    snprintf(msg, length, "%.1fMB", size/1000000.0);
  else if (size >= 1000)
    snprintf(msg, length, "%.1fKB", size/1000.0);
  else
    snprintf(msg, length, "%luB", size);

  msg[length-1] = '\0';
}


static int
findPrevSelected(void *item, void *cdata)
{
    return ((long)cdata == *(long*)(((WMListItem*)item)->text));
}


static int
findFirstUnread(void *item, void *cdata)
{
    MESSAGECACHE *msgcache = mail_elt(((MailBox*)cdata)->stream,
                                      *(long *)(((WMListItem*)item)->text));

    if (!msgcache)
        return 0;

    return !(msgcache->seen);
}


void
updateSummaryForUnseen(MailBox *mbox, long wasSelected)
{
  WMArray *array = WMGetListItems(mbox->summaryView);
  int pos;

  if (wasSelected) {
    int current = WMGetListSelectedItemRow(mbox->summaryView);
    int height = WMWidgetHeight(mbox->summaryView)/17;

    if(current < WMGetListPosition(mbox->summaryView)
      || current >= height + WMGetListPosition(mbox->summaryView)) {
printf("not visible %d %d %d\n", current, height, 
WMGetListPosition(mbox->summaryView));

        pos = WMFindInArray(array, findPrevSelected, (void*)wasSelected);
        if (pos != WANotFound) {
          WMSelectListItem(mbox->summaryView, pos);
          WMSetListPosition(mbox->summaryView, pos);
        }
      }
    } else {
        pos = WMFindInArray(array, findFirstUnread, (void*)mbox);
        if (pos != WANotFound) {
            WMSetListPosition(mbox->summaryView, pos);
        }
        clearMessageView(mbox->messageView);
        WMRefreshText(mbox->messageView);
    }
}


static void
paintSummaries(WMList *summaryView, int index, Drawable d,
  char *null, int state, WMRect *rect)
{
  WMListItem *item;
  int width, height, x, y, used_width, rem_width, i;
  char mnum[6], date[8], text[MAILTMPLEN];
  time_t t;
  MailBox *mbox;
  GC gc;
  unsigned long msgno;
  MESSAGECACHE *msgcache;
  BODY *body;

  assert((mbox = (MailBox *)WMGetHangedData(summaryView)));
  if(Aileron.mbox == mbox && Aileron.mboxLocked)
    return;

  item = WMGetListItem(mbox->summaryView, index);
  assert(mbox->stream && item);
  assert(item->text);

  msgno = *(long *)(item->text);
  assert(msgno >= 1 &&  msgno <= mbox->stream->nmsgs);

  assert((msgcache = mail_elt(mbox->stream, msgno)));

  snprintf(mnum, 5, "%i", index + 1);


  width = rect->size.width;
  height = rect->size.height;
  x = rect->pos.x;
  y = rect->pos.y;


  if (state & WLDSSelected) {
    XFillRectangle(Aileron.display, d, Aileron.whitegc,
      x, y, width, height);
    XDrawLine(Aileron.display, d, Aileron.darkgreygc, x,
      y, (x + width) - 1, y);
    XDrawLine(Aileron.display, d, Aileron.darkgreygc, x,
      (y + height) - 1, (x + width) - 1, (y + height) - 1);

    gc = (msgcache->deleted ? Aileron.greygc : Aileron.blackgc);

    if(!msgcache->seen)
      WMDrawPixmap(Aileron.npix_w, d, x + 6, y + 4);

#if 1
    mail_fetchstructure(mbox->stream, msgno, &body);
    if(body && body->type != TYPETEXT)
      WMDrawPixmap(Aileron.mpix_w, d, x + 16, y + 4);
#endif

    if(msgcache->answered)
      WMDrawPixmap(Aileron.apix_w, d, x + 26, y + 4);

  } else {
    XClearArea(Aileron.display, d, x, y, width, height, False);

    gc = (msgcache->deleted ? Aileron.darkgreygc : Aileron.blackgc);

    if(!msgcache->seen)
      WMDrawPixmap(Aileron.npix_g, d, x + 6, y + 4);

#if 1
    mail_fetchstructure(mbox->stream, msgno, &body);
    if(body && body->type != TYPETEXT)
      WMDrawPixmap(Aileron.mpix_g, d, x + 16, y + 4);
#endif

    if(msgcache->answered)
      WMDrawPixmap(Aileron.apix_g, d, x + 26, y + 4);
  }

  WMDrawString(Aileron.screen, d, gc, Aileron.listfont,
    x+(70 - WMWidthOfString(Aileron.listfont, mnum, strlen(mnum))),
    y+2, mnum, strlen(mnum));
  t = mail_longdate(msgcache);

  if(mbox->monthFirst)
    strftime(date, 7, "%d %b", localtime(&t));
  else
    strftime(date, 7, "%b %d", localtime(&t));

  WMDrawString(Aileron.screen, d, gc,
    Aileron.listfont, x+85, y+2, date, strlen(date));


  if(mbox->showSizes) {
    char size[10];
    sizeToString(msgcache->rfc822_size, 10, size);

    WMDrawString(Aileron.screen, d, gc,
      Aileron.listfont,  width-35, y + 2, size, strlen(size));

    used_width = 200;  /* the preceding drawn items plus spaces ... */

  } else {
    used_width = 140;
  }

  rem_width = (int) (0.5*(width-used_width))-10;
  mail_fetchfrom(text, mbox->stream, msgno, (long)MAILTMPLEN-1);

  if ((i = strlen(text)) > 0 ) {
    do --i; while(i>0 && text[i] == ' ');
    text[i+1] = 0;
  }

  if(*text)  {
    int c = canhold(text, rem_width);
    char *mark = strchr(text, '<');
    if(mark) {
      if((int)(mark-text) < c)
        c = (int)(mark-text);
    }

    WMDrawString(Aileron.screen, d, gc,
      Aileron.listfont, 160, y+2, text, c);
    x = WMWidthOfString(Aileron.listfont, text, c);
  }

  mail_fetchsubject(text, mbox->stream, msgno, (long)MAILTMPLEN-1);
  if(*text) {
    WMDrawString(Aileron.screen, d, gc,
      Aileron.listfont, 170+rem_width, y + 2,
      text, canhold(text, rem_width));
  } else {
    WMDrawString(Aileron.screen, d, gc,
      Aileron.listfont, 170+rem_width, y + 2,
      "<No subject>", canhold("<No subject>", rem_width));
  }

}



static void
paintStatusLabel(MailBox *mbox)
{
  char size[10], dsize[10], buf[50];
  Bool totals;

  assert(mbox);

  totals = WMGetUDBoolForKey(Aileron.defaultsDB, "ShowMessageTotals");

  if(mbox->isFocused) {
     if(mbox->numFocused>1)
       snprintf(buf, 49, "Focused on %lu messages", mbox->numFocused);
     else if(mbox->numFocused==1)
       snprintf(buf, 49, "Focused on 1 message");
     else if(mbox->numFocused==0)
       snprintf(buf, 49, "All message being focused on have been deleted");

     WMSetLabelText(mbox->statusl, buf);
     return;
  }


  if(mbox->nmsgs==0) {
    if(mbox->deleted>0) {
      char del[10];

      if(mbox->showSizes) {
        sizeToString(mbox->dsize, 10, dsize);
        snprintf(del, 10, " (%s)", dsize);
      } else snprintf(del, 10, " ");

      snprintf(buf, 49, "No messages -- all (%ld) deleted%s",
      mbox->deleted, del);
    } else {
      snprintf(buf, 49, "No messages ");
    }

  } else  {

    if(mbox->showSizes) {
      char del[10];


      sizeToString(mbox->size, 10, size);

      if(mbox->deleted > 0) {
        sizeToString(mbox->dsize, 10, dsize);
        snprintf(del, 10, " (%s)", dsize);
      } else snprintf(del, 10, " ");


      snprintf(buf, 49, "%ld message%s(%s) -- %ld deleted%s",
        mbox->nmsgs, mbox->nmsgs==1?" ":"s ",
          size, mbox->deleted, del);


    } else {
      if(mbox->deleted > 0) {
        snprintf(buf, 49, "%ld message%s -- %ld deleted",
        mbox->nmsgs, mbox->nmsgs==1?" ":"s ",
          mbox->deleted);
      } else {
        snprintf(buf, 49, "%ld message%s ",
          mbox->nmsgs, mbox->nmsgs==1?" ":"s ");
      }

    }
  }

  if(totals) {
    if(!mbox->statusMapped) {
      WMMapWidget(mbox->statusl);
      mbox->statusMapped = True;
    }
    WMSetLabelText(mbox->statusl, buf);

  } else {
    if(mbox->statusMapped) {
      WMUnmapWidget(mbox->statusl);
      mbox->statusMapped = False;
    }
  }
}



static void composeCB(XEvent *event, void *data)
{
  if (event->xbutton.button == Button1)
    ComposeMessage(CMCompose);
  else if(event->xbutton.button == Button2)
    ComposeMessage(CMReply);
  else
    ComposeMessage(CMForward);
}

static void findCB(WMWidget *w, void *client)
{
  //RunFindPanel();
}



static void upCB(WMWidget *w, void *client)
{
   NextMessage(client, next_UP);
}

static void downCB(WMWidget *w, void *client)
{
   NextMessage(client, next_DOWN);
}


static void mailboxesCB(WMWidget *w, void *client)
{
  RunMailboxesPanel();
}


static void getMailCB(WMWidget *w, void *client)
{
  MailBox *mbox = (MailBox *)client;
  unsigned long ocount, ncount;


  ocount = WMGetListNumberOfRows(mbox->summaryView);
  mail_ping(mbox->stream);
  ncount = mbox->stream->nmsgs;

  if(ocount != ncount)
    SortMailSummaries(mbox, mbox->sortBy, mbox->reverseSortOrder);
}


static void
pingForNewmail(void *data)
{
  MailBox *mbox = (MailBox *)data;
  int minutes;

  assert(mbox);
  assert(mbox->stream);

  if(!WMGetUDBoolForKey(Aileron.defaultsDB, "NewMailManual")) {
    minutes = WMGetUDIntegerForKey(Aileron.defaultsDB, "NewMailQueryMinutes");

    if(minutes > 0) {
      mbox->timer = WMAddTimerHandler(minutes*60000, pingForNewmail, mbox);

     } else if(mbox->timer) {
      WMDeleteTimerHandler(mbox->timer);
      mbox->timer = NULL;
    }
  } else if(mbox->timer) {
    WMDeleteTimerHandler(mbox->timer);
    mbox->timer = NULL;
  }

  if(!mbox->isFocused)
    getMailCB(NULL, mbox);
}




/* return codes:
     -1 : mailbox is opended by another process
      0 : mailbox is not opended
      1 : mailbox is opened by this procces
*/
int mailboxIsOpen(const char *name)
{
  MailBox *mbox = NULL;

  assert(name);

  mbox = WMHashGet(Aileron.windowTable, name);
  if(mbox) {
    getMailCB(NULL, mbox);
    WMMapWidget(mbox->window);
    WMRaiseWidget(mbox->window);
    Aileron.mbox = mbox;
    Aileron.cORm = 1;
    return 1;
  }


/* TODO: this really has to check the stream somehow... in case it's
not been opended by Aileron... */
 // return -1;


  return 0;
}

void closeWindow(WMWidget *self, void *client)
{
  MailBox *mbox = (MailBox *) client;

  assert(mbox);

  CloseMailBox(mbox->name);
}

void scrollMessage(void *vmbox, Bool direction)
{
  MailBox *mbox = (MailBox *)vmbox;

  if(!mbox)
    return;

  switch(direction) {
    case next_UP:
      if(!WMScrollText(mbox->messageView, -16))
        NextMessage((void *)mbox, next_UP);
    break;

    case next_DOWN:
      if(!WMScrollText(mbox->messageView, 16))
        NextMessage((void *)mbox, next_DOWN);
    break;

    case page_UP:
      if(!WMPageText(mbox->messageView, 0))
        NextMessage((void *)mbox, next_UP);
    break;

    case page_DOWN:
      if(!WMPageText(mbox->messageView, 1))
        NextMessage((void *)mbox, next_DOWN);
    break;
  }

}

static void
summaryViewAction(WMWidget *list, void *data)
{
  MailBox *mbox;
  WMListItem *item;

  mbox = (MailBox *)data;
  assert(mbox);
  assert(mbox->stream);

  item = WMGetListSelectedItem(mbox->summaryView);
  if(!item)
    return;

  assert(item->text);

  WMEmptyArray(mbox->currentAttachments);

  if(GetMessage((void *)mbox,  *(unsigned long *)(item->text),
    bestAlternative)) {
    MarkMessagesAsRead((void *)mbox, True);

  } else {
    clearMessageView(mbox->messageView);
    WMAppendTextStream(mbox->messageView,
      "<HTML><FONT color=red><B>Could not open message!<B></font></html>");
    WMThawText(mbox->messageView);
    return;
  }

}

static void
handleEnterLeave(XEvent *event, void *data)
{
/* need something better... like lastClicked in or something... */
  Aileron.mouseWithinSumary = (event->type == EnterNotify);
}

static void
handleKeyPress(XEvent *event, void *data)
{
  MailBox *mbox = (MailBox *)data;
  KeySym ksym;

  XLookupString(&event->xkey, NULL, 0, &ksym, NULL);

  if(event->xkey.state & XK_Alt_L &  8) {

  switch (ksym) {

    case XK_c :  EnterSelection(); break;
    case XK_f :  RunFindPanel(); break;
    case XK_g :  FindNext(); break;
    case XK_G:   FindPrevious(); break;

    case XK_h:   WMHideApplication(Aileron.appContext); break;
    case XK_H:   WMHideOthers(Aileron.appContext); break;
    case XK_n :  ComposeMessage(CMCompose); break;
    case XK_r :  ComposeMessage(CMReply); break;
    case XK_R :  ComposeMessage(CMReplyAll); break;
    case XK_W :  ComposeMessage(CMForward); break;
#if 0
    case XK_B :  ComposeMessage(CMBounce); break;
#endif
    case XK_p :  printf("RunPrintPanel\n"); break;
    case XK_q :  QuitAileron();
    case XK_s :  break;
    case XK_w : closeWindow(NULL, (void *) mbox); break;


    case XK_Up:  scrollMessage((void *)mbox, next_UP); break;
    case XK_Down:  scrollMessage((void *)mbox, next_DOWN);  break;

    default:  break;
    }

  } else if(event->xkey.state & XK_Shift_L & 15) {


  switch(ksym) {

    case XK_Delete:
    case XK_KP_Delete:
      WMPerformButtonClick(mbox->deleteb);
    break;

    case XK_Return:
        WMPerformButtonClick(mbox->upb);
    break;


    case XK_Down:
      scrollMessage((void *)mbox, next_DOWN);
    break;

    case XK_Up:
      scrollMessage((void *)mbox, next_UP);
    break;
  }
  } else {

  switch(ksym) {

    case XK_Down:
      WMPerformButtonClick(mbox->downb);
    break;

    case XK_Up:
      WMPerformButtonClick(mbox->upb);
    break;


    case XK_Page_Up:
      scrollMessage((void *)mbox, page_UP);
    break;

    case XK_Page_Down:
      scrollMessage((void *)mbox, page_DOWN);
    break;

    case XK_Delete:
    case XK_KP_Delete:
      WMPerformButtonClick(mbox->deleteb);
    break;

    case XK_u: MarkMessagesAsRead((void *)mbox, False); break;

    case XK_h :  {
      if(mbox->headerLevel<2) mbox->headerLevel++;
      else mbox->headerLevel=0;
      summaryViewAction(mbox->summaryView, mbox);
    }
    default: break;
  }
  }
}


static void markMessageAsDeletedCB(WMWidget *w, void *client)
{
  MailBox *mbox = (MailBox *)client;
  WMArray *items;

  items = GetMailBoxSelectedMessages(mbox);
  if (!items)
    return;

  if(WMGetArrayItemCount(items) == 1) {
    WMListItem *item;

    if((item = WMGetFromArray(items, 0))) {
      MESSAGECACHE *msgcache = mail_elt(mbox->stream, *(long *)(item->text));
      MarkMessagesAsDeleted(mbox, !msgcache->deleted);
    }

  } else {
    if(MarkMessagesAsDeleted(mbox, True) && 
      WMGetUDBoolForKey(Aileron.defaultsDB, "AutoNext"))
      NextMessage((void *)mbox, next_DOWN);
  }
}


/* it seems the WMList widget is _much_ faster at sorting
   than the c-client ... */

static int
sortSummariesByDate(const void *a, const void *b)
{
  WMListItem *item1 = *(WMListItem**)a;
  WMListItem *item2 = *(WMListItem**)b;
  MESSAGECACHE *msgcache1, *msgcache2;
  time_t t1, t2;
  MailBox *mbox = (MailBox *)Aileron.mbox;

  if(!item1 || !item2)
    return 0;

  if(!item1->text || !item2->text)
    return 0;

  msgcache1 = mail_elt(mbox->stream, *(long *)(item1->text));
  msgcache2 = mail_elt(mbox->stream, *(long *)(item2->text));

  if(!msgcache1 || !msgcache2)
    return 0;

  t1 = mail_longdate(msgcache1);
  t2 = mail_longdate(msgcache2);


  if(t1 > t2)
    return (mbox->reverseSortOrder?-1:1);
  else if(t1 < t2)
    return (mbox->reverseSortOrder?1:-1);
  else return 0;

}

static int
sortSummariesBySubject(const void *a, const void *b)
{
  WMListItem *item1 = *(WMListItem**)a;
  WMListItem *item2 = *(WMListItem**)b;
  char s1[256];
  char s2[256];
  MailBox *mbox = (MailBox *)Aileron.mbox;

  if(!item1 || !item2)
    return 0;

  if(!item1->text || !item2->text)
    return 0;

  mail_fetchsubject(s1, mbox->stream,
    *(long *)(item1->text), 255);
  mail_fetchsubject(s2, mbox->stream,
    *(long *)(item2->text), 255);

  if(!s1 || !s2)
    return 0;

  return (mbox->reverseSortOrder?-1:1)*(strcmp(s1,s2));
}

static int
sortSummariesBySender(const void *a, const void *b)
{
  WMListItem *item1 = *(WMListItem**)a;
  WMListItem *item2 = *(WMListItem**)b;
  char s1[256];
  char s2[256];
  MailBox *mbox = (MailBox *)Aileron.mbox;

  if(!item1 || !item2)
    return 0;

  if(!item1->text || !item2->text)
    return 0;

  mail_fetchfrom(s1, mbox->stream,
    *(long *)(item1->text), 255);
  mail_fetchfrom(s2, mbox->stream,
    *(long *)(item2->text), 255);

  if(!s1 || !s2)
    return 0;
  return (mbox->reverseSortOrder?-1:1)*(strcmp(s1,s2));
}

static int
sortSummariesBySize(const void *a, const void *b)
{
  WMListItem *item1 = *(WMListItem**)a;
  WMListItem *item2 = *(WMListItem**)b;
  MESSAGECACHE *msgcache1, *msgcache2;
  unsigned long s1, s2;
  MailBox *mbox = (MailBox *)Aileron.mbox;

  if(!item1 || !item2)
    return 0;

  if(!item1->text || !item2->text)
    return 0;

  msgcache1 = mail_elt(mbox->stream, *(long *)(item1->text));

  msgcache2 = mail_elt(mbox->stream, *(long *)(item2->text));
  if(!msgcache1 || !msgcache2)
    return 0;

  s1 = msgcache1->rfc822_size;
  s2 = msgcache2->rfc822_size;

  if(s1 > s2)
    return (mbox->reverseSortOrder?-1:1);
  else if(s1 < s2)
    return (mbox->reverseSortOrder?1:-1);
  else
    return 0;
}

static int
sortSummariesByArrival(const void *a, const void *b)
{
  WMListItem *item1 = *(WMListItem**)a;
  WMListItem *item2 = *(WMListItem**)b;
  MailBox *mbox = (MailBox *)Aileron.mbox;
  unsigned long i1, i2;

  if(!item1 || !item2)
    return 0;

  if(!item1->text || !item2->text)
    return 0;

  i1 = *(long *)(item1->text);
  i2 = *(long *)(item2->text);

  if(i1 < i2)
    return (mbox->reverseSortOrder?1:-1);
  else if(i1 > i2)
    return (mbox->reverseSortOrder?-1:1);
  else
    return 0;
}



void SortMailSummaries(void *vmbox, int type, int reverse)
{
  MailBox *mbox = (MailBox *)vmbox, *prevmbox = Aileron.mbox;
  unsigned long *msgPtr, l;
  MESSAGECACHE *msgcache;
  Bool showDel, netbox;
  BODY *body;
  long wasSelected;
  WMListItem *item;


  assert(mbox);
  assert(mbox->stream);

  if ((item = WMGetListSelectedItem(mbox->summaryView)))
      wasSelected = *(long *)(item->text);
  else
      wasSelected = 0;


  WMClearList(mbox->summaryView);
  mbox->nmsgs = mbox->stream->nmsgs;
  mbox->deleted = 0;
  mbox->size = 0;
  mbox->dsize = 0;

  while(Aileron.mboxLocked);
  Aileron.mbox = mbox;
  Aileron.mboxLocked = True;

  mbox->sortBy = type;
  mbox->reverseSortOrder = reverse;

  netbox = (*mbox->name == '{');
  showDel = WMGetUDBoolForKey(Aileron.defaultsDB, "ShowDeletedMessages");
  for (l=0; l < mbox->nmsgs; l++) {

    /* slow down and prefetch here, rather than when user is
       scrolling list */
    if(netbox)
      mail_fetchstructure(mbox->stream, l+1, &body);

    msgcache = mail_elt(mbox->stream, l+1);
    if(msgcache->deleted && !showDel) {
      mbox->deleted++;
      mbox->dsize += msgcache->rfc822_size;

    } else {
      if(msgcache->deleted) {
        mbox->deleted++;
        mbox->dsize += msgcache->rfc822_size;

      } else {
        mbox->size += msgcache->rfc822_size;
      }

      msgPtr = wmalloc(sizeof(long));
      *msgPtr = l+1;
      item = WMAddListItem(mbox->summaryView, " ");
      ((unsigned long *)item->text) = msgPtr;
    }
  }

  mbox->nmsgs -= mbox->deleted;

  switch(type) {

    case byDate:
      WMSortListItemsWithComparer(mbox->summaryView,
        sortSummariesByDate);
    break;

    case byArrival:
        WMSortListItemsWithComparer(mbox->summaryView,
          sortSummariesByArrival);
    break;

    case bySender:
        WMSortListItemsWithComparer(mbox->summaryView,
          sortSummariesBySender);
    break;

    case bySize:
        WMSortListItemsWithComparer(mbox->summaryView,
          sortSummariesBySize);
    break;

    case bySubject:
        WMSortListItemsWithComparer(mbox->summaryView,
          sortSummariesBySubject);
    break;
  }


  updateSummaryForUnseen(mbox, wasSelected);

  WMSetWindowDocumentEdited(mbox->window, !(mbox->saved = True));
  Aileron.mboxLocked = False;
  RefreshMailBoxSummary((void *) mbox);
  if(prevmbox)
    Aileron.mbox = prevmbox;
}

static int
parseHeader(char *text, char **items, int maxItems, int *longestHeadLen)
{
  int i, j=0, k=0, len=0;
  char tmp[MAILTMPLEN];

  len = strlen(text);
  for(i=0; i<len && j<=maxItems; i++) {

  /* spelt out:
    while we don't encounter a new line followed,
    (if i+2 is less than len)  by a space or tab,
    and k is not greater than the space within tmp,
    and the character is printable, append to tmp.
  */
    if( (text[i] != 13 ||
      ((i+2<len)?(text[i+2] == ' ' || text[i+2] == 9):0))
      && k<MAILTMPLEN-1) {
      if(isprint(text[i]))
        tmp[k++] = text[i];
    } else {
      tmp[k] = '\0';
      i++;
      k=0;
      items[j++] = wstrdup(tmp);
      *longestHeadLen = WMAX(*longestHeadLen, strlen(tmp));
    }
  }

  if(k>0) {
    tmp[k] = '\0';
    items[j] = wstrdup(tmp);
    *longestHeadLen = WMAX(*longestHeadLen, strlen(tmp));
  } else j--;


  return j;
}

void
freeHeaderBag(void *data)
{
  if(data)
    wfree(data);
}

static void
prependHeaders(WMText *tPtr, MAILSTREAM *stream, long msgno, int showall)
{
  WMPropList *array, *val;
  int nheaders, count, i;
  char *headerString, *headerList[20]; /* that many */
  STRINGLIST *lines, *cur;
  char hTitle[33], hd[MAILTMPLEN], format[MAILTMPLEN];
  WMBag *headerBag;
  int longestHeadLen=0;

  if(Aileron.defaultsDB && !showall)  {
    array = WMGetUDObjectForKey(Aileron.defaultsDB, "HeadersToShow");
    if (!array || !WMIsPLArray(array))
      return;

    count = WMGetPropListItemCount(array);
    lines = mail_newstringlist ();
    cur = lines;

    for (i=0; i<count; i++) {
      val = WMGetFromPLArray(array, i);

      cur->text.data = (unsigned char *) WMGetFromPLString(val);
      cur->text.size = strlen((char *)cur->text.data);
      cur = cur->next = mail_newstringlist();
    }

    headerString = wstrdup(mail_fetchheader_full(
      stream, msgno, lines, NIL, NIL)); /* _very_ nice feature */
                /* was: FT_NOT */

  } else {
    headerString = wstrdup(mail_fetchheader(stream, msgno));
  }


  if(strlen(headerString)<1) /* don't bother */
    return;

  headerBag = WMCreateTreeBagWithDestructor(freeHeaderBag);

  nheaders = parseHeader(headerString, headerList, 20, &longestHeadLen);
  if(longestHeadLen > 30)
    longestHeadLen = 30;
  for(i=0; i<nheaders; i++) {
    char *mark = strchr(headerList[i], ':');
    if(mark) {
      int c = (int)(mark-headerList[i]);
      if(c>31) c = 31;

      snprintf(hTitle, c+2, "%s:", headerList[i]);

/* flush all headers wif this */
/* non  fixed-width doesn't work well with this though... */
 longestHeadLen = 0;
      snprintf(format, MAILTMPLEN-1,
        "<HTML><BR><B>%%%ds</B> %%s</HTML>", longestHeadLen);
      snprintf(hd, MAILTMPLEN-1, format, hTitle, &(headerList[i])[c+2]);
    } else {
      snprintf(hd, MAILTMPLEN-1, headerList[i]);
    }

    WMPutInBag(headerBag, wstrdup(hd));
    free(headerList[i]);
  }
  free(headerString);


  {
    int count = WMGetBagItemCount(headerBag);
    int i;

    for(i=0; i<count; i++)
      WMAppendTextStream(tPtr, (char*)WMGetFromBag(headerBag, i));

    WMFreeBag(headerBag);
    WMAppendTextStream(tPtr, "<html><p><p></html>");
   }
}

static char *
addPrefix(char *src)
{
  char *dest = NULL, *mark, *start;
  char *pref = WMGetUDStringForKey(Aileron.defaultsDB, "ReplyPrefix");

  if(!src || !pref)
    return wstrdup(pref);

  start = src;
  while(start) {
    mark = strchr(start, '\n');
    if(mark) {
      int len = ((int)(mark-start));
      char c[len+2];
      memcpy(c, start, len);
      c[len] = '\n';
      c[len+1] = 0;
      dest = wstrappend(dest, pref);
      dest = wstrappend(dest, c);
      start = mark + 1;

    } else {
      int len = strlen(start);
      char c[len];
      memcpy(c, start, len);
      c[len] = '\n';
      c[len+1] = 0;
      dest = wstrappend(dest, pref);
      dest = wstrappend(dest, c);
      start = mark;
    }
  }

  return dest;
}


void
FetchMessagePart(WMText *tPtr, MAILSTREAM *stream,
  unsigned long msgno, BODY *body, char *pfx,
  unsigned long section, short alternative, short prefix)
{
  PART *part;
  unsigned long rawlen;
  char sec[MAILTMPLEN];
  Bool createFile=1;  /* create a file for the encoding */

  if (body->type == TYPEMULTIPART) {
    if (pfx)
      sprintf (sec,"%s%ld.", pfx, ++section);
    else
      sec[0] = '\0';

    for (section=0, part=body->nested.part; part; part=part->next)
      FetchMessagePart(tPtr, stream, msgno, &part->body,
        sec, section++, alternative, prefix);

    return;
  }



  if (!pfx)
    pfx = "";
  sprintf (sec, "%s%ld", pfx, ++section);

#if DEBUG
puts(sec);
#endif

  if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") &&
    (body = body->nested.msg->body)) {

    if (body->type == TYPEMULTIPART) {
      FetchMessagePart(tPtr, stream, msgno, body, pfx,
        section-1, alternative, prefix);
    } else {
      sprintf (sec, "%s%ld.", pfx, section);
      FetchMessagePart(tPtr, stream, msgno, body, sec,
        (long) 0, alternative, prefix);
    }

  } else {
    void * rawdata = (mail_fetchbody_full(stream, msgno,
      sec, &rawlen, FT_INTERNAL));

    if(!rawdata || rawlen < 1) {
      WMAppendTextStream(tPtr,
        "<HTML><FONT color=red><B>no data!?<B></font></html>");
      return;
    }

    switch (body->type) {

      case TYPETEXT: {
        int plain, wasplain;

        if(!(wasplain = Aileron.textIsPlain))
          plain = strncasecmp(body->subtype, "HTML", 4);


        if(body->encoding == ENC7BIT ||
          body->encoding == ENC8BIT ||
          body->encoding == ENCQUOTEDPRINTABLE) {

          ((char *)rawdata)[rawlen] = 0;
          if(plain)
            Aileron.textIsPlain = 1;

          if(prefix) {
            char *pdata = addPrefix(rawdata);
            WMAppendTextStream(tPtr, pdata);
            wfree(pdata);
          } else WMAppendTextStream(tPtr, (char *)rawdata);

          if(!wasplain && plain)
            Aileron.textIsPlain = 0;
          } else {
#if DEBUG
            printf("DEBUG: funny body encoding for text %d\n", body->encoding);
#endif
            createFile = 0;
            goto _TEXT_DECODE;
          }

        } break;


      case TYPEMULTIPART:
#if DEBUG
        printf("DEBUG... err... no TYPEMULTIPART here\n");
#endif
      break;

      case TYPEMESSAGE:
#if DEBUG
        printf("DEBUG... err... no TYPEMESSAGE here\n");
#endif
      break;

_TEXT_DECODE:
      default: {
        PARAMETER *par; /* the parameter of interest */
        void *decdata=NULL;  /* after decoding */
        unsigned long dlength; /* when decoded */
        FILE * file; /* the file to write decoded data to if needed */
        char *name=NULL; /* given in message, or runtime generated */
        int i, len, c;  /* to remove space from name */
        char path[MAILTMPLEN];  /* the default path dir + name */


        decdata = (void *)
          (body->encoding == ENCBASE64?
            rfc822_base64((unsigned char *)rawdata, rawlen, &dlength)
            : (body->encoding == ENCBINARY?
              rfc822_binary((unsigned char *)rawdata, rawlen, &dlength)
              : (body->encoding == ENC8BIT?
                rfc822_8bit((unsigned char *)rawdata, rawlen, &dlength)
                : (body->encoding == ENCQUOTEDPRINTABLE?
                  rfc822_qprint((unsigned char *)rawdata, rawlen, &dlength)
                  : NULL))));

        if(!decdata) {
          WMAppendTextStream(tPtr,
		    "<HTML><FONT color=red><p><B>can't decode part!<B></p></font>");
          return;
        }

        if(!createFile)  {
          ((char *) decdata)[dlength] = 0;
          WMAppendTextStream(tPtr, (char *) decdata);
          fs_give((void **) &decdata);
          return;
        }

        /* glean the name from the message, if it's given */
        if ((par = body->parameter)) do {
          if(!strncasecmp(par->attribute, "NAME", 4))
            name = wstrdup(par->value);
        } while ((par = par->next));

        if(!name) {  /* some clown sent a no-name attachment */
          if(body->description) { /* glean it from descr. */
            name = wstrdup(body->description);
          } else {
            name = wstrdup("AItmp.XXXXXX");
            mkstemp(name);  /* last resort, generate one */
          }
        }

        /* only letter, numbers, '.' and '_' allowed here */
        for(i=0, len=strlen(name); i<len; i++) {
          c = name[i];
          if(!isalnum(c) && c!='.' && c!='_')
            name[i] = '_';
        }

        /* create the full path */
        snprintf(path, MAILTMPLEN-1, "%s/%s", Aileron.tmpdir, name);
        wfree(name);

        if(!(file = fopen(path, "w"))) {
          char complaint[MAILTMPLEN];

          snprintf(complaint, MAILTMPLEN-1,
            "<html><b>Could not open the file [%s]"
            " to save it, so it can't be viewed</b></html>", path);
            WMAppendTextStream(tPtr, complaint);
            return;
        }


        fwrite(decdata, dlength, 1, file);
        fclose(file);

        if(Aileron.mbox) {
          MailBox *mbox = (MailBox *)Aileron.mbox;
          if(mbox->stream == stream) {
            WMPushInArray(mbox->currentAttachments,
              wstrdup(strrchr(path, '/')));
          }
        }

        fs_give((void **) &decdata);


        switch(body->type) {
          case TYPEIMAGE:  {
            char tbString[MAILTMPLEN];
            snprintf(tbString, MAILTMPLEN-1,
             "<html><img src=%s></html>", path);
            WMAppendTextStream(tPtr, tbString);
          } break;

          case TYPEAUDIO:
            WMAppendTextBlock(tPtr, WMCreateTextBlockWithPixmap(
              tPtr, Aileron.SoundIcon, path,
              WMDarkGrayColor(Aileron.screen), 0, 0));
          break;

          case TYPEVIDEO:
            WMAppendTextBlock(tPtr, WMCreateTextBlockWithPixmap(
              tPtr, Aileron.MovieIcon, path,
              WMDarkGrayColor(Aileron.screen), 0, 0));
          break;

          case TYPEAPPLICATION:
            WMAppendTextBlock(tPtr, WMCreateTextBlockWithPixmap(
              tPtr, Aileron.ApplicationIcon, path,
              WMDarkGrayColor(Aileron.screen), 0, 0));
          break;

          default:
            WMAppendTextBlock(tPtr, WMCreateTextBlockWithPixmap(
              tPtr, Aileron.UnknownIcon, path,
              WMDarkGrayColor(Aileron.screen), 0, 0));
          break;
        }


      }
    }
  }

}


Bool
GetMessage(void *vmbox, unsigned long msgno, int alternative)
{
  MailBox *mbox = (MailBox *)vmbox;
  BODY *body;
  MESSAGECACHE *msgcache;

  if(!mbox)
    return False;

  WMFreezeText(mbox->messageView);

  clearMessageView(mbox->messageView);

  mail_fetchstructure(mbox->stream, msgno, &body);
  if(!body) {
    WMAppendTextStream(mbox->messageView,
      "<HTML><FONT color=red><B>Could not open message body!<B></font></html>");

  } else {
    if(mbox->headerLevel || mbox->showAll) {
      int showall = mbox->showAll;
      if(mbox->headerLevel == 2)
         showall = True;
      prependHeaders(mbox->messageView, mbox->stream, msgno, showall);
    }
    FetchMessagePart(mbox->messageView, mbox->stream, msgno, body,
      NIL, (long)0, alternative, 0);
  }

#if 0
#4  0x807305e in W_CallDestroyHandlers (view=0x40cc9f60) at wevent.c:520
#5  0x8088647 in destroyView (view=0x40cc9f60) at wview.c:430
#6  0x80886d5 in W_DestroyView (view=0x40cc9f60) at wview.c:473
#7  0x8088623 in destroyView (view=0x40c67f60) at wview.c:422
#8  0x80886d5 in W_DestroyView (view=0x40c67f60) at wview.c:473
#9  0x8088623 in destroyVi
//fix wmtext and WM destroying same widget twice
  WMAppendTextStream(mbox->messageView,
    "<html><button src=\"mailboxes.tif\" h=64, w=64></html>");
#endif
  msgcache = mail_elt(mbox->stream, msgno);
  SetTimeStampWithTimeT(mbox->timestamp, mail_longdate(msgcache));

  WMThawText(mbox->messageView);

  if(WMGetUDBoolForKey(Aileron.defaultsDB, "AutoSaveAttachments"))
    SaveAllAttachments(mbox);

  return True;
}


void ShowMessageSizes()
{
  MailBox *mbox = (MailBox *) Aileron.mbox;

  if(!mbox)
    return;

  mbox->showSizes = !mbox->showSizes;
  RefreshMailBoxSummary((void *) mbox);
}


void ShowDeletedMessages(void)
{
  MailBox *mbox = (MailBox *) Aileron.mbox;

  if(!mbox)
    return;

  mbox->showDeleted = !mbox->showDeleted;
  SortMailSummaries(mbox, mbox->sortBy, mbox->reverseSortOrder);
}


void RefreshMailBoxes(void)
{
  MailBox *mbox;
  int i, count;

  count = WMGetBagItemCount(Aileron.openMboxes);
  if(count < 1)
    return;

  for(i=0; i<count; i++) {
    mbox = (MailBox *)WMGetFromBag(Aileron.openMboxes, i);
    RefreshMailBoxSummary((void *) mbox);
  }
}

void
SetMailBoxWindowTitle(void *vmbox, char *title)
{
  MailBox *mbox = (MailBox *)vmbox;

  assert(mbox && title);
  WMSetWindowTitle(mbox->window, title);
}

void
RestoreMailBoxWindowTitle(void *vmbox)
{
  MailBox *mbox = (MailBox *)vmbox;
  char *title;

  assert(mbox);
  title = wstrdup("Aileron: ");

  if(strlen(mbox->name)>4 && *mbox->name == '{' ) {
    char *tmp, *mark;
    int len;


    mark = strchr(mbox->name, '}');
    if( (int)(strchr(mbox->name, 0) - mark) > 1) {
      title = wstrappend(title, mark+1);
    } else title = wstrappend(title, "Inbox");

    title = wstrappend(title, " on ");

    tmp = mbox->name+1;
    mark = strchr(tmp, '/');
    assert(mark && mark > tmp);
    len = (int)(mark - tmp);
    { char _title[len+1];

      memcpy(_title, tmp, len);
      _title[len] = 0;
      title = wstrappend(title, _title);

    }


    title = wstrappend(title,
      (strstr(mbox->name, "/imap")?" (imap)":" (pop)"));
    WMSetWindowMiniwindowTitle(mbox->window,
     (strstr(mbox->name, "/imap")?"(imap)":"(pop)"));

  } else {
    char *minit;

    title = wstrappend(title, mbox->name);
    if((minit = strrchr(mbox->name, '/')))
      WMSetWindowMiniwindowTitle(mbox->window, &minit[1]);
    else
      WMSetWindowMiniwindowTitle(mbox->window, title);

  }

  WMSetWindowTitle(mbox->window, title);
  wfree(title);
}


Bool OpenDefaultMailBoxes(void)
{
  WMPropList *tarray, *val;
  int i, num;
  WMArray *array;

  tarray = WMGetUDObjectForKey(Aileron.defaultsDB, "DefaultMailBoxes");
  if (!tarray || !WMIsPLArray(tarray))
    return 0;

  /* using WMGetFromPLArray directly fails, need to save to WMArray! (tmp) */
  num = WMGetPropListItemCount(tarray);
  array = WMCreateArrayWithDestructor(num, releaseArrayItems);
  for (i=0; i<num; i++) {
    val = WMGetFromPLArray(tarray, i);
    if(!val)
      continue;

    if(WMGetFromPLString(val))
      WMPushInArray(array, wstrdup(WMGetFromPLString(val)));

  }

  num = WMGetArrayItemCount(array);
  for (i=0; i<num; i++) {
    OpenMailBox(WMGetFromArray(array, i));
  }

  WMFreeArray(array);
  return (num>0);
}





void OpenMailBox(const char *name)
{
  MailBox *mbox;
  unsigned short w=472, h=460;
  char *path;
  WMButton *b;
  WMFrame *frame;
  WMPixmap *pixmap;
  XClassHint *classHint;
  WMColor *color;
  WMSize size;
  WMPoint pos;
  int splitsize;

  if (!name)
    return;

  switch(mailboxIsOpen(name)) {
    case -1:
      RunAlertPanel("Open Mailbox",
        "This mailbox is currently in use by another program,\n"
        "so it will not be opened here.",
        "I understand", NULL, NULL);
    case 1:
      return;
  }


  mbox = (MailBox *) wmalloc(sizeof(MailBox));
  if (!mbox)
    return;


  mbox->headerLevel = 1;
  mbox->timer = NULL;
  mbox->showSizes =
    WMGetUDBoolForKey(Aileron.defaultsDB, "ShowMessageSizes");
  mbox->showDeleted =
    WMGetUDBoolForKey(Aileron.defaultsDB, "ShowDeletedMessages");
  mbox->monthFirst =
    WMGetUDBoolForKey(Aileron.defaultsDB, "MonthFirstInDate");


  mbox->name = wstrdup((char *)name);

  mbox->stream = mail_open(NIL, mbox->name, OP_DEBUG);
  if(!mbox->stream) {
    RunMailBoxAlertPanel(mbox->name);
    wfree(mbox->name);
    wfree(mbox);
    return;
  }


  mbox->window = WMCreateWindow(Aileron.screen, "Aileron MailBox");
  WMSetWindowMiniwindowPixmap(mbox->window,
    WMGetApplicationIconPixmap(Aileron.screen));
  WMAddNotificationObserver(sizeChanged, mbox,
    WMViewSizeDidChangeNotification, WMWidgetView(mbox->window));

  WMCreateEventHandler(WMWidgetView(mbox->window), KeyPressMask,
    handleKeyPress, mbox);
  WMSetWindowMinSize(mbox->window, 472, 360);


  if(GetGeometriesFor(mbox->name, &size, &pos, &splitsize)) {
    w = (size.width >= 472)? size.width:472;
    h = (size.height >= 360)? size.height:360;

   WMSetWindowInitialPosition(mbox->window, pos.x, pos.y);
   WMSetWindowUserPosition(mbox->window, pos.x, pos.y);

  /* yeh... splitview is braindead too */
    splitsize+=12;
  } else splitsize = 102;


  WMResizeWidget(mbox->window, w, h);
  WMRealizeWidget(mbox->window);
  WMSetViewNotifySizeChanges(WMWidgetView(mbox->window), True);


  classHint = XAllocClassHint();
  classHint->res_name = "MailBox";
  classHint->res_class = "Aileron";
  XSetClassHint(Aileron.display, WMWidgetXID(mbox->window), classHint);
  XFree(classHint);

  frame = WMCreateFrame(mbox->window);
  WMSetFrameRelief(frame, WRFlat);
  WMSetFrameTitlePosition(frame, WTPNoTitle);
  WMMoveWidget(frame, 8, 8);
  WMResizeWidget(frame, WMWidgetWidth(mbox->window) - 16, 82);

  mbox->upb = b = WMCreateCustomButton(frame,
    WBBPushInMask|WBBSpringLoadedMask|WBBPushLightMask);
  WMSetButtonAction(b, upCB, mbox);
  WMMoveWidget(b, 21, 0);
  WMResizeWidget(b, 64, 31);
  path = WMPathForResourceOfType("upbtn.xpm", "images/win");
  if(path) {
    pixmap = WMCreatePixmapFromFile(Aileron.screen, path);
    WMSetButtonImage(b, pixmap);
    WMSetButtonImagePosition(b, WIPImageOnly);
    WMReleasePixmap(pixmap);
    wfree(path);
  }


  mbox->downb = b = WMCreateCustomButton(frame,
    WBBPushInMask|WBBSpringLoadedMask|WBBPushLightMask);
  WMSetButtonAction(b, downCB, mbox);
  WMMoveWidget(b, 21, 33);
  WMResizeWidget(b, 64, 31);
  path = WMPathForResourceOfType("downbtn.xpm", "images/win");
  if(path) {
    pixmap = WMCreatePixmapFromFile(Aileron.screen, path);
    WMSetButtonImage(b, pixmap);
    WMSetButtonImagePosition(b, WIPImageOnly);
    WMReleasePixmap(pixmap);
    wfree(path);
  }


  mbox->deleteb = b = CreateBlendedButton(frame, WBTMomentaryPush,
    "Delete", "delete.tif");
  if(mbox->stream->rdonly)
    WMSetButtonEnabled(b, False);
  WMMoveWidget(b, 93, 0);
  WMSetButtonAction(b, markMessageAsDeletedCB, mbox);
  WMSetBalloonTextForView(
    "Click while holding down shift to undelete selected messages.",
    WMWidgetView(b));

  mbox->composeb = b = CreateBlendedButton(frame, WBTMomentaryPush,
    "Compose", "compose.tif");
  WMMoveWidget(b, 165, 0);
  WMCreateEventHandler(WMWidgetView(b), ButtonPressMask,
    composeCB, NULL);
  WMSetBalloonTextForView(
    "Click with the middle mouse button to reply to a message.\n"
    "Click with the third mouse button to foward a message.\n",
    WMWidgetView(b));

  b = CreateBlendedButton(frame, WBTMomentaryPush,
    "Mailboxes", "mailboxes.tif");
  WMMoveWidget(b, 237, 0);
  WMSetButtonAction(b, mailboxesCB, NULL);


  if(!WMGetUDBoolForKey(Aileron.defaultsDB, "NewMailManual")) {
    b = CreateBlendedButton(frame, WBTMomentaryPush,
      "Get Mail", "newmail.tif");
    WMMoveWidget(b, 309, 0);
    WMSetButtonAction(b, getMailCB, mbox);
  } else {
    b = CreateBlendedButton(frame, WBTMomentaryPush,
      "Find", "find.tif");
    WMMoveWidget(b, 309, 0);
    WMSetButtonAction(b, findCB, NULL);
  }


  mbox->timestamp = CreateTimeStamp(frame);
  WMMoveWidget(mbox->timestamp, 382, 0);
  SetTimeStampWithTimeT(mbox->timestamp, time(NULL));



  mbox->statusl = WMCreateLabel(frame);
  WMSetLabelImagePosition(mbox->statusl, WIPNoImage);
  WMSetLabelFont(mbox->statusl, Aileron.buttonfont);
  color = WMDarkGrayColor(Aileron.screen);
  WMSetLabelTextColor(mbox->statusl, color);
  WMReleaseColor(color);
  WMMoveWidget(mbox->statusl, 0, 69);
  WMResizeWidget(mbox->statusl, 250, 12);

  mbox->split = WMCreateSplitView(mbox->window);
  WMMoveWidget(mbox->split, 8, 91);
  WMResizeWidget(mbox->split, w-16, splitsize);
  WMSetViewNotifySizeChanges(WMWidgetView(mbox->split), True);
  WMSetSplitViewConstrainProc(mbox->split, splitViewConstrainCallback);


  mbox->summaryView = WMCreateList(mbox->window);
  WMSetListAllowEmptySelection(mbox->summaryView, True);
  WMSetListAllowMultipleSelection(mbox->summaryView, True);
  WMResizeWidget(mbox->summaryView, w-16, splitsize);
  WMSetListUserDrawProc(mbox->summaryView, paintSummaries);
  WMSetListUserDrawItemHeight(mbox->summaryView, 16);
  WMHangData(mbox->summaryView, mbox);
  WMSetListAction(mbox->summaryView, summaryViewAction, mbox);
  WMSetViewNotifySizeChanges(WMWidgetView(mbox->summaryView), True);
  WMSetBalloonTextForView("See \"Reading mail\" in the Help Panel",
    WMWidgetView(mbox->summaryView));
  WMCreateEventHandler(WMWidgetView(mbox->summaryView),
    EnterWindowMask | LeaveWindowMask, handleEnterLeave, mbox);
  WMAddNotificationObserver(sizeChanged, mbox,
    WMViewSizeDidChangeNotification, WMWidgetView(mbox->summaryView));



  mbox->messageView = WMCreateTextForDocumentType(mbox->window,
    HTMLParser, NULL); //HTMLWriter);
  WMSetTextHasRuler(mbox->messageView, True);
  WMSetTextHasVerticalScroller(mbox->messageView, True);
  WMSetTextRelief(mbox->messageView, WRSunken);
  WMSetTextDelegate(mbox->messageView, &textDelegate);
  WMCreateEventHandler(WMWidgetView(mbox->messageView), KeyPressMask,
    handleKeyPress, mbox);

  WMResizeWidget(mbox->messageView, w-16, h-splitsize);
  WMSetTextEditable(mbox->messageView, False);
  WMSetViewNotifySizeChanges(WMWidgetView(mbox->messageView), True);
  WMAddNotificationObserver(sizeChanged, mbox,
    WMViewSizeDidChangeNotification, WMWidgetView(mbox->messageView));

  WMSetBalloonTextForView("See \"Reading mail\" in the Help Panel",
    WMWidgetView(mbox->messageView));

  WMAddSplitViewSubview(mbox->split, WMWidgetView(mbox->summaryView));
  WMAddSplitViewSubview(mbox->split, WMWidgetView(mbox->messageView));

  WMMoveWidget(mbox->messageView, 0,
    WMWidgetHeight(mbox->summaryView)
    + WMGetSplitViewDividerThickness(mbox->split));

  WMResizeWidget(mbox->split, w-16, h-91);

  mbox->saved = True;
  mbox->showAll = False;
  mbox->isFocused = False;
  mbox->numFocused = 0;
  mbox->currentAttachments = WMCreateArrayWithDestructor(4, releaseArrayItems);

  mbox->sortBy =
    WMGetUDIntegerForKey(Aileron.defaultsDB, "SortingMethod");
  mbox->reverseSortOrder =
    WMGetUDBoolForKey(Aileron.defaultsDB, "ReverseSortOrder");

  mbox->requestClose = 0; /* in case user requests it */
  /* sets everything else up, then CreateSummary */
  pingForNewmail(mbox);
  if(mbox->requestClose) {
    WMDestroyWidget(mbox->window);
    mail_close(mbox->stream);
    wfree(mbox->name);
    wfree(mbox);
    return;
  }


  WMHashInsert(Aileron.windowTable, (char *) mbox->name, mbox);
  WMPutInBag(Aileron.openMboxes, (void *)mbox);
  WMSetWindowCloseAction(mbox->window, closeWindow, (void *) mbox);

  XSaveContext(Aileron.display, WMWidgetXID(mbox->window),
    Aileron.xContext, (XPointer) wstrappend(wstrdup("MBOX"), mbox->name));



  WMRealizeWidget(mbox->window);
  WMMapSubwidgets(frame);
  WMMapSubwidgets(mbox->split);
  WMMapSubwidgets(mbox->window);
  WMMapWidget(mbox->window);
  WMAppAddWindow(Aileron.appContext, WMWidgetXID(mbox->window));

  RestoreMailBoxWindowTitle(mbox);

  if(WMWidgetHeight(mbox->summaryView) <= 5)
    WMUnmapWidget(mbox->summaryView);
  if(WMWidgetHeight(mbox->messageView) <= 5)
    WMUnmapWidget(mbox->messageView);

}



void CloseMailBox(char *name)
{
  MailBox *mbox;
  Bool autoCompact;
  WMSize size;
  WMPoint pos;


  assert(name);

  mbox = WMHashGet(Aileron.windowTable, name);
  if(!mbox)
    return;

  if(mbox->timer)
    WMDeleteTimerHandler(mbox->timer);

  autoCompact = WMGetUDBoolForKey(Aileron.defaultsDB, "AutoCompact");

  if(!mbox->saved && mbox->deleted>0 && !autoCompact) {
    int result = RunAlertPanel("Close Mailbox",
      "This mailbox contains recently deleted messages.\n"
      "Do you want to remove them permanently?",
      "Yes", "No", NULL);
    mbox->stream = mail_close_full(mbox->stream, result?NIL:CL_EXPUNGE);

  } else {
    mbox->stream = mail_close_full(mbox->stream, autoCompact?CL_EXPUNGE:NIL);
  }


  if(!Aileron.aileronQuitting)
    WMRemoveFromBag(Aileron.openMboxes, (void *)mbox);

  WMFreeArray(mbox->currentAttachments);
  XDeleteContext(Aileron.display,
    WMWidgetXID(mbox->window), Aileron.xContext);
  WMHashRemove(Aileron.windowTable, name);

  size.width = WMWidgetWidth(mbox->window);
  size.height = WMWidgetHeight(mbox->window);
  pos = WMGetViewScreenPosition(WMWidgetView(mbox->window));
  /* apparently, WMGetViewPosition.x != WindowInitialPosition.x
    it's offset by the thickness of title bar and mebbe edge resistance...
    oh well, it will just appear lower than before :-P ! */
  if(pos.y>23)
    pos.y -= 23;
  SaveGeometriesFor(mbox->name, size, pos,
    WMWidgetHeight(mbox->summaryView));
  WMDestroyWidget(mbox->window);
  wfree(mbox->name);
  wfree(mbox);
  Aileron.mbox = NULL;
}



void RunMailBoxAlertPanel(char *mailbox)
{
  char *message;
  int result;

  if(Aileron.ignoreError) {
    Aileron.ignoreError = False;
    return;
  }

  if(Aileron.errorMsg) {
    message = wstrdup("`");
    message = wstrappend(message, mailbox);
    message = wstrappend(message, "' ");
    message = wstrappend(message,
      "\ncould not be opened for the following reason:\n");
    message = wstrappend(message, Aileron.errorMsg);
  } else {
    message = wstrdup(
      "There was no reason given for why\n`");
    message = wstrappend(message, mailbox);
    message = wstrappend(message, "'\ncould not be opened.");
  }

/* too long an error message */
#if 0
  message = wstrappend(message,
    "\nYou can choose to ignore this error and try again.");
#endif

  result = RunAlertPanel("Open Mailbox", message, "Ignore", "Quit", "Help");

  wfree(message);

  if(result==1)
    QuitAileron();
  else if(result==-1)
    RunHelpPanel(253);
}


void MarkMailBoxAsReadOnly(void *vmbox, Bool rdonly)
{
  MailBox *mbox = (MailBox *)vmbox;
  
  if(!mbox)
    return;
    
  // && rdonly
  RunAlertPanel("MailBox Open", "Another process has opened this mailbox.\n"
    "This Mailbox will be now read only" ,"I understand", NULL, NULL);
    
  //if(mbox->stream->rdonly)
  WMSetButtonEnabled(mbox->deleteb, False);
  
}
 


void NextMessage(void *vmbox, Bool direction)
{
  int total, current, height;
  MailBox *mbox = (MailBox *)vmbox;

  if(!mbox)
    return;

  if(direction == next_SAME) {
    mbox->showAll = True;
    summaryViewAction(mbox->summaryView, mbox);
    mbox->showAll = False;
    return;
  }

  current = WMGetListSelectedItemRow(mbox->summaryView);
  total = WMGetListNumberOfRows(mbox->summaryView);
  height = WMWidgetHeight(mbox->summaryView)/17;

  if(direction == next_DOWN) {
    if(++current < total) {
      WMUnselectListItem(mbox->summaryView, current-1);
      WMSelectListItem(mbox->summaryView, current);
      if(current >= height + WMGetListPosition(mbox->summaryView))
        WMSetListPosition(mbox->summaryView, current-height+1);
      summaryViewAction(mbox->summaryView, mbox);
    }


  } else if(direction == next_UP) {
    if(--current >= 0) {
      WMUnselectListItem(mbox->summaryView, current+1);
      WMSelectListItem(mbox->summaryView, current);
      if(current < WMGetListPosition(mbox->summaryView))
        WMSetListPosition(mbox->summaryView, current);
      summaryViewAction(mbox->summaryView, mbox);
    }
  }

}


Bool MarkMessagesAsDeleted(void *vmbox, Bool del)
{
  MailBox *mbox = (MailBox *)vmbox;
  WMArray *items;
  WMListItem *item;
  unsigned long msgno;
  MESSAGECACHE *msgcache;
  char buf[10];
  int i;
  int msidlen = strlen("Mail System Internal Data");
  int ddtmlen = strlen("DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA");

  assert(mbox);

  if(mbox->stream->rdonly)
    return False;

  items = WMGetListSelectedItems(mbox->summaryView);
  if(WMGetArrayItemCount(items) < 1)
    return False;

  clearMessageView(mbox->messageView);
  WMThawText(mbox->messageView);

  for(i=0; i < WMGetArrayItemCount(items);  i++) {
    assert((item = WMGetFromArray(items, i)));
    assert(item->text);

    msgno = *(long *)(item->text);
    msgcache = mail_elt(mbox->stream, msgno);
    assert(msgcache);

    if(*mbox->name == '{' && strstr(mbox->name, "/pop")) {
      char frm[MAILTMPLEN], sjt[MAILTMPLEN];

      mail_fetchfrom(frm, mbox->stream, msgno, (long)MAILTMPLEN-1);
      mail_fetchsubject(sjt, mbox->stream, msgno, (long)MAILTMPLEN-1);
      if(!strncmp(frm, "Mail System Internal Data", msidlen)
         && !strncmp(sjt,
            "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA", ddtmlen)){ 
        continue; /* pop3 doesn't mask this, so we catch it here */
      }
    }

    if(del && !msgcache->deleted) {
      sprintf(buf, "%ld", msgno);
      mail_setflag(mbox->stream, buf, "\\DELETED");
      if(mbox->nmsgs > 0) {
        mbox->size -= msgcache->rfc822_size;
        mbox->dsize += msgcache->rfc822_size;
        mbox->nmsgs--;
        mbox->deleted++;
        if(mbox->isFocused && mbox->numFocused>0)
            mbox->numFocused--;
      }

      if(!mbox->showDeleted) {
printf("DEBUG need to remove from list\n");
/* trouble is i here is not really the item's row in list */
#if 0
        WMRemoveListItem(mbox->summaryView, );
        WMUnselectListItem(mbox->summaryView, ); 
#endif
      }

    } else if(!del && msgcache->deleted) {
      sprintf(buf, "%ld", msgno);
      mail_clearflag(mbox->stream, buf, "\\DELETED");
      if(mbox->nmsgs < mbox->stream->nmsgs) {
        mbox->size += msgcache->rfc822_size;
        mbox->dsize -= msgcache->rfc822_size;
        mbox->nmsgs++;
        mbox->deleted--;

        if(mbox->isFocused && mbox->numFocused
              < WMGetArrayItemCount(WMGetListItems(mbox->summaryView)))
          mbox->numFocused++;
      }
    }
  }


  if(mbox->saved)
    WMSetWindowDocumentEdited(mbox->window, !(mbox->saved = False));

  RefreshMailBoxSummary((void *)mbox);

  return (WMGetListSelectedItemRow(mbox->summaryView)
    <= WMGetListNumberOfRows(mbox->summaryView)-1);

}



void MarkMessagesAsAnswered(void *vmbox, Bool answered)
{
  MailBox *mbox = (MailBox *)vmbox;
  unsigned long msgno;
  WMListItem *item;
  WMArray *items;
  MESSAGECACHE *msgcache;
  char buf[10];
  int i;

  assert(mbox);

  if(mbox->stream->rdonly)
    return;

  items = WMGetListSelectedItems(mbox->summaryView);
  if(WMGetArrayItemCount(items) < 1)
    return;

  for(i=0; i < WMGetArrayItemCount(items);  i++) {
    assert((item = WMGetFromArray(items, i)));
    assert(item->text);

    msgno = *(long *)(item->text);
    msgcache = mail_elt(mbox->stream, msgno);
    assert(msgcache);

    if(answered && !msgcache->answered) {
      sprintf(buf, "%ld", msgno);
      mail_setflag(mbox->stream, buf, "\\ANSWERED");

    } else if(!answered && msgcache->answered) {
      sprintf(buf, "%ld", msgno);
      mail_clearflag(mbox->stream, buf, "\\ANSWERED");
    }
  }

  RefreshMailBoxSummary((void *)mbox);

}


void MarkMessagesAsRead(void *vmbox, Bool read)
{
  MailBox *mbox = (MailBox *)vmbox;
  unsigned long msgno;
  WMListItem *item;
  WMArray *items;
  MESSAGECACHE *msgcache;
  char buf[10];
  int i;

  assert(mbox);

  if(mbox->stream->rdonly)
    return;

  items = WMGetListSelectedItems(mbox->summaryView);
  if(WMGetArrayItemCount(items) < 1)
    return;

  for(i=0; i < WMGetArrayItemCount(items);  i++) {
    assert((item = WMGetFromArray(items, i)));
    assert(item->text);

    msgno = *(long *)(item->text);
    msgcache = mail_elt(mbox->stream, msgno);
    assert(msgcache);

    if(read && !msgcache->seen) {
      sprintf(buf, "%ld", msgno);
      mail_clearflag(mbox->stream, buf, "\\NEW");
      mail_setflag(mbox->stream, buf, "\\SEEN");

    } else if(!read && msgcache->seen) {
      sprintf(buf, "%ld", msgno);
      mail_clearflag(mbox->stream, buf, "\\SEEN");
      mail_setflag(mbox->stream, buf, "\\RECENT");
    }
  }

  RefreshMailBoxSummary((void *)mbox);

}


void FocusMailBox(void *vmbox, Bool focus)
{
  MailBox *mbox = (MailBox *)vmbox;
  WMArray *array;
  WMListItem *item;
  int i;

  if(!mbox)
    return;

  if(!focus && !mbox->isFocused)
    return;

  if(focus && WMGetListSelectedItemRow(mbox->summaryView) == WLNotFound) {
    RunAlertPanel("Focus",
      "At least one message needs to be selected... "
      "just what are you focusing on?", "I understand", NULL, NULL);
    return;
  }

  array = WMGetListItems(mbox->summaryView);

  if(focus) {
    char title[256];

    snprintf(title, 255, "Aileron: Focused on %s", mbox->name);
    WMSetWindowTitle(mbox->window, title);

    for(i=0; i < WMGetArrayItemCount(array);  i++) {
      if(!(item = WMGetFromArray(array, i)))
        return;

      if(!item->selected) {
        WMRemoveListItem(mbox->summaryView, i);
        i--;
      }
    }
    mbox->numFocused = WMGetArrayItemCount(array);

  } else {
    SortMailSummaries(mbox, mbox->sortBy, mbox->reverseSortOrder);
    RestoreMailBoxWindowTitle((void *)mbox);
    mbox->numFocused = 0;
  }

  mbox->isFocused = focus;
  paintStatusLabel(mbox);
}


void CompactMailBox(void *vmbox)
{
  MailBox *mbox = (MailBox *)vmbox;

  assert(mbox);
  assert(mbox->stream);

  if(mbox->stream->rdonly)  {
    RunAlertPanel("Compact Mailbox", "This mailbox appears "
      "to be read only.\nCompacting is not possible.",
      "I understand", NULL, NULL);
    return;
  }

  mail_expunge(mbox->stream);
  clearMessageView(mbox->messageView);

  SortMailSummaries(mbox, mbox->sortBy, mbox->reverseSortOrder);
}




void RefreshMailBoxSummary(void *vmbox)
{
  MailBox *mbox = (MailBox *)vmbox;

  assert(mbox);
  paintStatusLabel(mbox);
  /* used to force redraw, so there :-P */
  WMSetListUserDrawItemHeight(mbox->summaryView, 16);
}

unsigned long
GetMailBoxTotalMessages(void *vmbox)
{
  MailBox *mbox = (MailBox *)vmbox;

  assert(mbox);

  return mbox->nmsgs;
}

unsigned long
GetMailBoxSelectedMessage(void *vmbox)
{
  WMListItem *item;
  MailBox *mbox = (MailBox *)vmbox;

  if(!mbox)
    return 0;

  item = WMGetListSelectedItem(mbox->summaryView);
  if(!item)
    return 0;

  assert(mbox->stream);
  assert(item->text);

  return *(long *)(item->text);
}

WMArray *GetMailBoxSelectedMessages(void *vmbox)
{
  WMArray *items;
  MailBox *mbox = (MailBox *)vmbox;

  assert(mbox);
  assert(mbox->stream);

  items = WMGetListSelectedItems(mbox->summaryView);
  if(!items)
    return NULL;

  return items;
}


void *GetMailBoxForName(char *name)
{
  assert(name);
  return WMHashGet(Aileron.windowTable, name);
}

WMList *GetMailBoxSummaryView(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->summaryView;
}


WMText *GetMailBoxMessageView(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->messageView;
}

WMWindow *GetMailBoxWindow(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->window;
}

MAILSTREAM *GetMailBoxStream(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->stream;
}

int GetMailBoxSortType(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->sortBy;
}

int GetMailBoxReverseSort(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->reverseSortOrder;
}

char *GetMailBoxName(void *vmbox)
{
  assert(vmbox);
  return ((MailBox *)vmbox)->name;
}


Bool MailBoxIsNetwork(void *vmbox)
{
  assert(vmbox);
  return ( *(((MailBox *)vmbox)->name) == '{');
}

Bool MailBoxIsIMAP(void *vmbox)
{
  MailBox *mbox;

  assert(vmbox);

  mbox = (MailBox *)vmbox;
  return (*mbox->name == '{' && strstr(mbox->name, "/imap"));
}

Bool MailBoxIsPOP(void *vmbox)
{
  MailBox *mbox;

  assert(vmbox);

  mbox = (MailBox *)vmbox;
  return (*mbox->name == '{' && strstr(mbox->name, "/pop"));
}

void SaveAllAttachments(void *vmbox)
{
  MailBox *mbox = (MailBox *)vmbox;
  int i, c;
  char *fname, *command=NULL, *path=NULL;
  FILE *pipe;
  struct stat status;

  assert(mbox);
  fname = WMGetUDStringForKey(Aileron.defaultsDB, "SaveAttachmentsTo");
  if(!fname) {
    path = SetEntity(mbox->window, "SaveAttachmentsTo", 1,
      "Set Attachment Folder", 1);
    if(!path)
      return;
  } else {
    path = wstrdup(fname);
  }


  if(stat(path, &status) != 0) {
    char *complaint = wstrdup("The folder \"");

    complaint = wstrappend(complaint, path);
    complaint = wstrappend(complaint, "\" does not exist.");
    RunAlertPanel("Save All Attachments",
      complaint, "I understand", NULL, NULL);
    wfree(complaint);
    wfree(path);
    return;
  }

  if(!S_ISDIR(status.st_mode)) {
    char *complaint = wstrdup("The entity \"");

    complaint = wstrappend(complaint, path);
    complaint = wstrappend(complaint, "\" is not a folder.");
    RunAlertPanel("Save All Attachments",
      complaint, "I understand", NULL, NULL);
    wfree(complaint);
    wfree(path);
    return;
  }

  c = WMGetArrayItemCount(mbox->currentAttachments);
  for(i=0; i < c;  i++) {
    if(!(fname = WMGetFromArray(mbox->currentAttachments, i)))
       continue;

    command = wstrdup("cp ");
    command = wstrappend(command, Aileron.tmpdir);
    command = wstrappend(command, "/");
    command = wstrappend(command, fname);
    command = wstrappend(command, " ");
    command = wstrappend(command, path);
    command = wstrappend(command, "/");
    command = wstrappend(command, fname);

    if(!(pipe = popen(command, "w"))) {
      wfree(command);
      continue;
    }

    pclose(pipe);
    wfree(command);
  }

  wfree(path);
}


/* c-client Main Program Callbacks */
void mm_flags(MAILSTREAM *stream, unsigned long num)
{
  //printf ("mm_flags: %d\n", num);
}

void mm_searched(MAILSTREAM *stream, unsigned long num)
{
  //printf ("mm_searched: %d\n", num);
}

void mm_expunged(MAILSTREAM *stream, unsigned long num)
{
  //printf ("mm_expunged: %d\n", num);
}

void mm_exists(MAILSTREAM *stream, unsigned long num)
{

}




void mm_notify(MAILSTREAM *stream, char *string, long errflg)
{
  //printf ("mm_notify: %s\n", string);
}


void mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status)
{
  //printf ("mm_status: %s\n", mailbox);
}

void mm_log(char *string, long errflg)
{
  if(errflg == ERROR && string) {
    if(Aileron.errorMsg)
      wfree(Aileron.errorMsg);
    Aileron.errorMsg = wstrdup(string);
   }

 // printf ("mm_log: %s [%ld]\n", string, errflg);
}

void mm_dlog(char *string)
{
  //printf ("mm_dlog: %s\n", string);
}

void mm_login (NETMBX *mb, char *user, char *pwd, long trials)
{
  char *passwd = NULL;
  if(trials >= 3) {
    pwd[0] = 0;
    RunAlertPanel("Open Network MailBox",
      "This mailbox cannot be opened since the maximum number of "
      "attempts to authenticate you has been reached.",
      "I understand", NULL, NULL);
    return;
  }

#if 0
   getPasswordEntryFor(mbox, user, pwd);
   if(*user != 0) /* found in hashtable */
     return;
#endif

//from prefs, use MD5? if(auth_md5_valid());
  if(mb->user[0]) {
    strncpy(user, mb->user, MAILTMPLEN-1);
  } else {
    char *username = WMRunInputPanel(Aileron.screen, NULL,
      "Open Network MailBox",
      "Type in user name for", myusername(), "Set", "Cancel");

    if(!username) {
      pwd[0] = 0;
      return;
    }

    strncpy(user, username, MAILTMPLEN-1);
    wfree(username);
  }

  passwd = RunPasswordPanel(Aileron.screen, NULL,
    "Open Network MailBox",
    (trials == 0?"Type in account password.":
      "Server didn't like the previous password; again:"),
    NULL, "Set", "Cancel", mb->host);

  if(passwd && strlen(passwd) > 0 ) {
    int len = strlen(passwd);
    strncpy(pwd, passwd, MAILTMPLEN-1);
    memset(passwd, 0, len);
    wfree(passwd);

  } else {
    Aileron.ignoreError = True;
    pwd[0] = 0;
    return;
  }
}


long mm_diskerror(MAILSTREAM *stream, long errcode, long serious)
{
  printf ("mm_diskerror: %ld\n", serious);
  return 0;
}


void mm_fatal(char *string)
{
  printf("mm_fatal %s\n", string);
}


void mm_list (MAILSTREAM *stream, int delimiter, char *mailbox, long
attributes)
{
  printf ("mm_list: %s\n", mailbox);
}



