/* ml.c */ 

#include "ml.h"
#include <locale.h>

/* global structures and variables */

XtAppContext context;
Preferences preferences;
Local_Auth local_auth;
Session *session;        /* Most everything in the program is found here */
Display *display;
int errfd[2];
Boolean no_prefs = FALSE;
Boolean silent_pipe = FALSE; 
char MLSender[FILEBUFFLEN];
char MLMailbox[FILEBUFFLEN];

char **Argv;
int Argc = 0;


XtActionsRec global_actions[] = {
  { "complete-address", (XtActionProc) complete_address },
  { "search-text",      (XtActionProc) search_text      },
  { "hex-input",        (XtActionProc) hex_input        },
  { "lview-pop",        (XtActionProc) lview_pop        },
  { "read-pop",         (XtActionProc) read_pop         },
  { "compose-pop",      (XtActionProc) compose_pop      },
  { "filters-pop",      (XtActionProc) filters_pop      },
  { "mailboxes-pop",    (XtActionProc) mailboxes_pop    },
  { "mailcopy-pop",     (XtActionProc) mailcopy_pop     },
  { "addresses-pop",    (XtActionProc) addresses_pop    },
  { "note-pop",         (XtActionProc) note_pop         },
  { "help-pop",         (XtActionProc) help_pop         },
  { "netconf-pop",      (XtActionProc) netconf_pop      },
  { "messages-pop",     (XtActionProc) messages_pop     },
};

/* IMAP registered drivers */

extern DRIVER 
imapdriver,   mboxdriver,  bezerkdriver, tenexdriver,    nntpdriver, 
newsdriver,   dummydriver, pop3driver,   mtxdriver,      mhdriver,      
mmdfdriver,   philedriver;


/* Begin Program */

#ifdef __STDC__
void main(int argc,char **argv, char **envp)
#else
void main(argc,argv,envp)
     int argc;
     char ** argv;
     char ** envp;
#endif
{
  Arg args[ARGLISTSIZE];
  MainWindow *mwin;
  LogWindow  *lwin;
  int fd;
  int n = 0;
  char buffer[FILEBUFFLEN];
  char envstring[256];
  struct stat st;
  char *ptr;
  Argv = argv;
  Argc = argc;


  if(getenv(ML_SETLANG))
#if XtSpecificationRelease > 4
    XtSetLanguageProc(NULL, NULL, NULL); 
#else
    setlocale(LC_ALL, EMPTYSTR); 
#endif

  session = (Session *)    fs_get(sizeof(Session));
  mwin    = (MainWindow *) fs_get(sizeof(MainWindow));
  mwin->next = mwin->prev = NULL;

  session->lwin    = (LogWindow *)  fs_get(sizeof(LogWindow));
  session->active = NULL;
  session->mwin = mwin;

  /***********************************************************************
   *              Create top level application shell - start X           *
   ***********************************************************************/

  session->shell = mwin->shell = XtAppInitialize((XtAppContext*) &context,
						 PROGRAM,
						 NULL, 0, &argc,argv, 
#ifdef FALLBACK
						 fallback_resources, 
#else /* FALLBACK */
						 NULL,
#endif /* FALLBACK */
						 args, n);      
  n = 0;

  setup_editres(mwin->shell);

  XtAppAddActions(context, global_actions, XtNumber(global_actions));

  if((get_user()) == SYSCALL_FAILURE)  {
    fprintf(stderr,"Cannot determine user identity. Program terminated.");
    exit(0);
  }

  (void) chdir(local_auth.homedir);

  mail_link(&imapdriver);  mail_link(&mboxdriver);  mail_link(&bezerkdriver);
  mail_link(&tenexdriver); mail_link(&nntpdriver);  mail_link(&newsdriver);
  mail_link(&pop3driver);  mail_link(&mtxdriver);   mail_link(&mhdriver);
  mail_link(&mmdfdriver);  mail_link(&philedriver); mail_link(&dummydriver);


  session->connected = TRUE;          /* optimistic assumption */
  session->connections = 0;
  session->address_book = NULL;
  session->addresses = NULL;
  session->edfilter = NULL;
  session->filters = NULL;
  session->session_filters = NULL;
  session->servers = NULL;
  session->mime_handlers = NULL;
  session->busy = FALSE;
  session->buttonstate = BTN_NOMAILBOXESOPEN;
  session->authorizations = NULL;
  session->netconf = NULL;
  session->mailboxwin = NULL;
  session->mailcopywin = NULL;
  session->compose = NULL;
  session->read = NULL;
  session->prefst = NULL;
  session->pref2st = NULL;
  session->gentext = NULL;
  session->update_needed = 0;
  session->filter_map = NULL;
  session->note = NULL;
  session->btn = NULL;
  session->header_set = NULL;
  session->header_str = NULL;
  session->last_command = NULL;
  session->last_dir = NULL;
  session->mailboxes = NULL;

  (void) get_resources(session->shell);

  /* Honor the NNTPSERVER variable unless told otherwise. */

  if((ptr = getenv("NNTPSERVER")) != NULL) 
      preferences.nntp_server = cpystr(ptr);

  /* Fill in all the language specific structures. */

  init_representation();
  init_flags();
  init_action_and_header_list();
  init_logical_view_structures();

  /*
   *  Create a cache directory. Maybe there's already one. If we fail,
   *  or it doesn't have permissions we can use, complain loudly and die
   *  horribly.
   */

  sprintf(buffer,"%s/%s",local_auth.homedir,MLDATADIR);
  (void) mkdir(buffer,S_IRWXU);
  if((access(buffer,R_OK|W_OK|X_OK)) != SYSCALL_SUCCESS) {
    fprintf(stderr,MLGetLocalized(XtNmsgNoDataDir,MsgNoDataDir),buffer);
    exit(1);
  }
  session->cachedir = cpystr(buffer);
  
  if((load_defaults()) == SYSCALL_FAILURE)
    no_prefs = TRUE;
  (void) sane_defaults();

  if(preferences.authLogin == TRUE)
    auth_link(&auth_log);

  /* Load these after the preferences, so we know where to look. */

  (void) load_mime_types();
  (void) load_mime_extension_map();
  (void) load_mime_handlers();

  /* Initialize the c-client's sense of identity. Ignore results. */

  (void) myusername_full(NIL);

  display = XtDisplay(session->shell);

  make_icons();

  (void) create_main_window(mwin);

  create_log_window(session->lwin);



  (void) create_cursors(display);


  if(ml_icon != (Pixmap) None)
    XtVaSetValues(session->shell, XmNiconPixmap, ml_icon, NULL);

  /* 
   * For the poor folks on X terminals and PC's who start the
   * program as "ml -display {something}" and wonder why their 
   * attachment handlers and other sub-processes don't work.
   */

  sprintf(envstring,"DISPLAY=%s",XDisplayString(display));
  (void) putenv(envstring);

  /* Sorry, but this one's in English. Period. */

  sprintf(buffer,"Welcome to %s, Version %s\n",PROGRAM,MLVERSION);
  log_error(buffer, MLNOTIFY);

  if(preferences.showLog == FALSE) {
    /* show the banner for a couple of seconds and then nuke the log window */
    sleep(2);
    lwin_dismiss(NULL,session->lwin,NULL);
  }
  
  trap_stderr();

  /* Debug a quirky locale */

  if(getenv("MLDEBUG")) {
    fprintf(stderr,"Locale: %s\n",setlocale(LC_ALL, NULL));
    fflush(stderr);
  }

  /* Check for obsolete or missing app-defaults. */

  if((preferences.local_defs == FALSE) 
     && (preferences.defaults_installed == FALSE))
    mm_log(MLGetLocalized(XtNmsgNoDefaults,MsgNoDefaults),WARN);
  else {
    if((strcmp(preferences.defaults_version,DEFAULTS_VERSION)) != STRMATCH)
      mm_log(MLGetLocalized(XtNmsgWrongDefaults,MsgWrongDefaults),WARN);
  }

  if(preferences.auto_open == TRUE)
    auto_open(mwin);

  /*************************************************************************
   *           Main application loop - get and process input forever       *
   *************************************************************************/

  main_loop();
  /* NOTREACHED */
}



#ifdef __STDC__
MAILSTREAM *server_to_mailstream(char *server)
#else
MAILSTREAM *server_to_mailstream(server)
     char *server;
#endif
{
  Mailbox *mailbox;
  if((server == NULL) || (*server == NUL_TERM))
    return(NULL);

  for(mailbox = session->mailboxes; mailbox; mailbox = mailbox->next)
    if((strcasecmp(mailbox->host,server)) == STRMATCH)
      return(mailbox->mailstream);
  return(NULL);
}

/* 
 * auto_open has to initialize a few things in the mailbox structure
 * which would have otherwise been initialized by the mailbox select window.
 */


#ifdef __STDC__
void auto_open(MainWindow *mwin)
#else
void auto_open(mwin)
     MainWindow *mwin;
#endif
{
  Mailbox *mailbox = new_mailbox();
  Boolean new_server = FALSE;
  Server_Config *config;

  if(*preferences.defaultConfig == NUL_TERM) {
    fs_give((void **) &mailbox);
    return;
  }
  config = find_configuration(preferences.defaultConfig);
  mailbox->host = cpystr((config->hostname) ? config->hostname : EMPTYSTR);
  mailbox->mailboxname = fix_mailboxpath((config->mailbox) 
					 ? config->mailbox : INBOX);
  mailbox->imapname = 
    format_mailboxname_server(config,mailbox->mailboxname,MAILBOX_TYPE_MAIL);
  mailbox->type = MAILBOX_TYPE_MAIL;

  if((mailbox->imapname == NULL) || (*mailbox->imapname == NUL_TERM)) {
    free_mailbox(mailbox);
    return;
  }

  mailbox->server = config;
  session->active = config;
  open_mailbox(mwin, mailbox, new_server);
  session->active = NULL;
  return;
}


#ifdef __STDC__
void open_mailbox(MainWindow *mwin, Mailbox *mailbox, Boolean new_server)
#else
void open_mailbox(mwin, mailbox, new_server)
     MainWindow *mwin;
     Mailbox *mailbox;
     Boolean new_server;
#endif
{
  char log[FILEBUFFLEN];
  char popname[FILEBUFFLEN];

  Lview *lview;
  int total = 0;
  long openflags = 0L;
  struct stat st;
  int fd;

  if(preferences.developer_debug == TRUE)
    openflags |= OP_DEBUG;

  push_cursor(WATCH_CURSOR);

  if((mailbox->imapname == NULL) || (*mailbox->imapname == NUL_TERM)) {
    free_mailbox(mailbox);
    mwin_cancel(mwin);
    pop_cursor();
    return;
  }

  /* 
   * Just in case the mailbox is already open, we'll be nice and close it.
   */

  close_invalid_mailbox(mailbox->imapname);
  if((session->active != NULL) && (session->active->type == SERVICE_POP)) {   
    sprintf(popname,"%s/%s/%s.inbox",local_auth.homedir,MLDATADIR,
	    (session->active->name) ? session->active->name : "" );
    if((access(popname,R_OK|W_OK)) != SYSCALL_SUCCESS) {
      fd = open(popname,O_CREAT|O_RDWR,0700);
      if(fd)
	close(fd);
    }
    mailbox->ispop = TRUE;
  }
  else
    mailbox->ispop = FALSE;

  sprintf(log,MLGetLocalized(XtNmsgOpeningMailbox,MsgOpeningMailbox),
	  mailbox->mailboxname);
  mm_log(log,NIL);

  if(((mailbox->mailstream = 
      mail_open(mailbox->mailstream,
		(mailbox->ispop) ? popname : mailbox->imapname,
		openflags)) == NIL) 
     || (mailbox->mailstream->halfopen)) {
    if(mailbox->mailstream)
      mail_close(mailbox->mailstream);
    free_mailbox(mailbox);
    mwin_cancel(mwin);
    mm_log(MLGetLocalized(XtNmsgOpenFailed,MsgOpenFailed),ERROR);
    pop_cursor();
    return;
  }

  mailbox->nmsgs = mailbox->mailstream->nmsgs;
  if(session->mailboxes)
    session->mailboxes->prev = mailbox;
  mailbox->next = session->mailboxes;
  session->mailboxes = mailbox;

  if(preferences.check_interval)
    mailbox->timer = 
      XtAppAddTimeOut(context,
		      ((unsigned long) preferences.check_interval * 1000L),
		      (XtTimerCallbackProc) timed_check, mailbox);
  else
    mailbox->timer = (XtIntervalId) 0;

  create_all_lview(mwin,mailbox);
  session->buttonstate = BTN_ON;
  view_check_buttons();

  pop_cursor();
  return;
}

#ifdef __STDC__
void check_mailbox(Mailbox *mailbox, Boolean startup)
#else
void check_mailbox(mailbox, startup)
     Mailbox *mailbox;
     Boolean startup;
#endif
{
  MAILSTREAM *popstream = NIL;
  char msg[FILEBUFFLEN];
  char popname[FILEBUFFLEN];
  char sequence[16];
  unsigned long i;
  unsigned long delta = 0;

  if((session->busy == TRUE) && (startup == FALSE))
    return;

  push_cursor(WATCH_CURSOR);

  if(mailbox->ispop) {
    session->active = mailbox->server;
    if(((popstream = mail_open(popstream,mailbox->imapname,OP_SILENT)) == NIL)
       || (popstream->halfopen))
      mail_close(popstream);
    else {
      sprintf(popname,"%s/%s/%s.inbox",local_auth.homedir,MLDATADIR,
	      (session->active->name) ? session->active->name : "" );
      for(i = 1; i <= popstream->nmsgs; i ++) {
	if(((copy_message_to_cachemailbox(popstream,i,popname)) 
	    != SYSCALL_SUCCESS))
	  break;
	delta ++;
	if(preferences.keepOnServer == FALSE) {
	  sprintf(sequence,"%lu",i);
	  mail_setflag(popstream,sequence,DELETED_FLAG);
	}
      }
      if(delta) {
	mail_expunge(popstream);
	mail_ping(mailbox->mailstream);
      }
      mail_close(popstream);
    }
    session->active = NULL;
  }
  if(startup == TRUE) {
    pop_cursor();
    return;
  }

  if(mail_ping(mailbox->mailstream)) {
    if(mailbox->has_new_mail == TRUE) {
      get_new_message_info(mailbox);
      mailbox->has_new_mail = FALSE;
      if(preferences.newMailBeep == TRUE)
	XBell(display,1000);
    }
    if(mailbox->flags_changed != 0L) {
      mail_check(mailbox->mailstream);
      mailbox->flags_changed = 0L;
    }
  }
  else {
    sprintf(msg,MLGetLocalized(XtNmsgLostConnection,MsgLostConnection),
	    mailbox->host); 
    mm_log(msg,ERROR);
  }
  pop_cursor();
  return;
}

#ifdef __STDC__
void timed_check(Mailbox *mailbox, XtIntervalId *id)
#else
void timed_check(mailbox,id)
     Mailbox *mailbox;
     XtIntervalId *id;
#endif
{

  unsigned long next_timer;
 
  /* 
   * We've been called by a timer (or user) to check the mailbox.
   * Unfortunately, we might be doing something critical.
   * If we are, set the timer interval to a short period (15 seconds)
   * so we'll keep coming back until things are free again.
   * Otherwise, check the mailbox, and reset to the normal timer interval.
   */

  if(session->busy == FALSE) { 
    check_mailbox(mailbox, FALSE);
    next_timer = (unsigned long) preferences.check_interval * 1000L;
  }
  else
    next_timer = (unsigned long) 15000L;

  mailbox->timer = XtAppAddTimeOut(context,next_timer,
				   (XtTimerCallbackProc) timed_check,
				   mailbox);
  return;
}


#ifdef __STDC__
void main_close(Widget w,XtPointer data,XtPointer xp)
#else
void main_close(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  Mailbox *mailbox, *next;
  mm_log(MLGetLocalized(XtNmsgClosingAll,MsgClosingAll),NIL);

  for(mailbox = session->mailboxes; mailbox ; mailbox = next) {
    next = mailbox->next;
    close_mailbox(mailbox);
  }
  session->mailboxes = NULL;
  return;
}


#ifdef __STDC__
void close_mailbox(Mailbox *mailbox)
#else
void close_mailbox(mailbox)
     Mailbox *mailbox;
#endif
{
  Lview *lview;
  MainWindow *mwin;
  Read *read;
  Read_Info *read_info;
  Boolean found = FALSE;
  char log[FILEBUFFLEN];
  char sequence[64];

  sprintf(log,
	  MLGetLocalized(XtNmsgClosingMailbox, MsgClosingMailbox), 
	  mailbox->imapname);
  mm_log(log,NIL);

  if(mailbox->timer)
    XtRemoveTimeOut(mailbox->timer);
  mailbox->timer = (XtIntervalId) 0;
  if(mailbox->bg_timer)
    XtRemoveTimeOut(mailbox->bg_timer);
  mailbox->bg_timer = (XtIntervalId) 0;

  if(mail_ping(mailbox->mailstream)) {
    if((mailbox->type == MAILBOX_TYPE_NEWS) 
       && (mailbox->nmsgs > 0L)
       && (mailbox->no_update == FALSE)) {
      if(mailbox->nmsgs == 1L)
	strcpy(sequence,"1");
      else
	sprintf(sequence,"1:%lu",(unsigned long) mailbox->nmsgs);
      mm_log(MLGetLocalized(XtNmsgUpdatingNewsrc,MsgUpdatingNewsrc),NIL);
      mail_setflag(mailbox->mailstream,sequence,DELETED_FLAG);
      XmUpdateDisplay(session->shell);
    }
    mail_check(mailbox->mailstream);
    mail_close(mailbox->mailstream);
  }
  
  compose_mailbox_close(mailbox->mailstream);
  mwin_close_mailbox(mailbox);
  
  if(session->read) {
    do {
      found = FALSE;
      for(read = session->read; read; read = read->next) {
	for(read_info = read->read_info; read_info; 
	  read_info = read_info->prev) {
	  if(read_info->mailstream == mailbox->mailstream) {
	    if(read_info == read->current)
	      read_close_kill_current(read->shell,read,NULL);
	    else
	      kill_read_info(read_info);
	    found = TRUE;
	  }
	}
      }
    } while(found == TRUE);
  }


  if(mailbox == session->mailboxes)
    session->mailboxes = mailbox->next;
  
  if(mailbox->prev)
    mailbox->prev->next = mailbox->next;
  if(mailbox->next)
    mailbox->next->prev = mailbox->prev;
  
  free_mailbox(mailbox);
  mailbox = NULL;
  if(session->mailboxes == NULL) {
    if(session->edfilter && session->edfilter->is_realized)
      edfilter_dismiss(NULL, session->edfilter, NULL);
    session->buttonstate |= 
      BTN_NOSELECTION|BTN_NOMAILBOXESOPEN|BTN_NOPREV|BTN_NONEXT;
    view_check_buttons();
  }  
  XFlush(display);
  return;
}



#ifdef __STDC__
void main_compose(Widget w, XtPointer data, XtPointer xp)
#else
void main_compose(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  (void) compose_message(COMPOSE_NEW, FALSE, NULL, NULL, NULL);
  return;
}

#ifdef __STDC__
void main_address_book(Widget w, XtPointer data, XtPointer xp)
#else
void main_address_book(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  (void) create_address_book_window(session->shell);
  return;
}


#ifdef __STDC__
void main_note_book(Widget w, XtPointer data, XtPointer xp)
#else
void main_note_book(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  (void) create_note_window();
  return;
}

#ifdef __STDC__
void main_logwin(Widget w, XtPointer data, XtPointer xp)
#else
void main_logwin(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  (void) lwin_popup(session->shell, session->lwin, NULL);
  return;
}


#ifdef __STDC__
void no_op(Widget w, XtPointer data, XtPointer xp)
#else
void no_op(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  /* globally accessible absolutely useless function */
  return;
}

#ifdef __STDC__
void main_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell,MAINHELPFILE);
  return;
}


#ifdef __STDC__
void main_keyboard_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_keyboard_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell, KEYBOARDHELPFILE);
  return;
}

#ifdef __STDC__
void main_config_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_config_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell, MIMEINFOHELPFILE);
  return;
}

#ifdef __STDC__
void main_resources_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_resources_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell, RESOURCESHELPFILE);
  return;
}

#ifdef __STDC__
void main_intl_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_intl_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell, INTLHELPFILE);
  return;
}


#ifdef __STDC__
void main_version_help(Widget w, XtPointer data, XtPointer xp)
#else
void main_version_help(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  help(session->shell, VERSIONHELPFILE);
  return;
}

/* 
 * End the program. Unless you kill it, or it (shudder) dumps core,
 * this is the only way out. 
 */

#ifdef __STDC__
void bye(Widget w, XtPointer data, XtPointer xp)
#else
void bye(w,data,xp)
     Widget w;
     XtPointer data;
     XtPointer xp;
#endif
{
  if(session->mailboxes)
    main_close(w,NULL,NULL);
  exit(0);
}

/*
 * This is called out of our main loop when something changed during
 * a callback. refresh_needed means that the current contents of
 * the view window changed; we have to do something because the 
 * change is visible. update_needed means we have to re-evaluate
 * our logical view filters. They may or may not be changed as a 
 * result.
 */  


#ifdef __STDC__
void update_views(void)
#else
void update_views()
#endif
{
  Mailbox *mailbox;
  for(mailbox = session->mailboxes; mailbox; mailbox = mailbox->next) {
    if(mailbox->refresh_needed) {
      update_current_view(mailbox);
      mailbox->refresh_needed = 0;
    }
    if(mailbox->update_needed) {
      update_logical_views(mailbox,FALSE);
      mailbox->update_needed = 0;
    }
  }
  return;
}


/* 
 * Grab a chunk of messages envelopes in the "background".
 * We'll keep doing this at a settable interval until the mailbox
 * is completely loaded, or a newsgroup has been pruned.
 */


#ifdef __STDC__
void background_fetch(Mailbox *mailbox, XtIntervalId *id)
#else
void background_fetch(mailbox,id)
     Mailbox *mailbox;
     XtIntervalId *id;
#endif
{
  Boolean result;
  unsigned long count;

  if(session->busy == FALSE) { 
    if(mailbox->first_fetch <= 1L) {    /* no more to fetch */
      update_logical_views(mailbox,TRUE);
      mailbox->update_needed = 0;
      XtRemoveTimeOut(mailbox->bg_timer);
      mailbox->bg_timer = (XtIntervalId) 0;
      mm_log(MLGetLocalized(XtNmsgBackgroundDone,MsgBackgroundDone),MLNOTIFY);
      return;
    }
    else {                             /* more to fetch */
      push_cursor(WATCH_CURSOR);
      if(mailbox->type == MAILBOX_TYPE_MAIL) {
	if(mailbox->first_fetch <= preferences.mail_fetch)
	  mailbox->first_fetch = 1L;
	else
	  mailbox->first_fetch -= preferences.mail_fetch;
      }
      else {  /* MAILBOX_TYPE_NEWS */
	if(mailbox->first_fetch <= preferences.news_fetch)
	  mailbox->first_fetch = 1L;
	else
	  mailbox->first_fetch -= preferences.news_fetch;
      }
      count = mailbox->first_fetch;
      do {
	result = background_fetch_message(mailbox,count, FALSE);
	count ++;
      } while(result == TRUE);

      update_current_view(mailbox);
      mailbox->refresh_needed = 0;
      if(mailbox->first_fetch <= 1L) {   /* We've just gotten the last one */
	update_logical_views(mailbox,TRUE);
	mailbox->update_needed = 0;
	XtRemoveTimeOut(mailbox->bg_timer);
	mailbox->bg_timer = (XtIntervalId) 0;
	mm_log(MLGetLocalized(XtNmsgBackgroundDone,MsgBackgroundDone),
	       MLNOTIFY);
	pop_cursor();
	return;
      }
      pop_cursor();
    }
  }

  /* Adios for now, but we'll be back. */

  mailbox->bg_timer = XtAppAddTimeOut(context,
				      (mailbox->type == MAILBOX_TYPE_NEWS) 
				      ? (preferences.news_background * 1000)
				      : (preferences.mail_background * 1000),
				      (XtTimerCallbackProc) background_fetch,
				      mailbox);
  return;
}


/* 
 * This loop is called from our modal pop-ups -- those that wait
 * for you to do something before continuing. The loop is exited
 * when the condition (usually an int) becomes non-zero. This usually
 * happens when a modal widget gets what it needs and relinquishes
 * exclusive control of the keyboard and mouse by killing itself.
 * In general we set "pirate" cursors for other windows to indicate
 * that they are non-functional until the active window is dismissed.
 * Yes, it's awful design strategy, but necessary in this program,
 * since something you are doing elsewhere (like expunging messages 
 * or closing mailboxes) could invalidate the entire state of the 
 * current data structures.
 */

#ifdef __STDC__
void modal_main_loop (int *exit_condition)
#else
void modal_main_loop (exit_condition)
     int *exit_condition;
#endif
{
  XEvent event;

  while(!(*exit_condition)) {
    if(XPending(display)) {
      XtAppNextEvent(context, &event);
      XtDispatchEvent(&event);
    }
  }
  return;
}


/*
 * This is the normal event processing loop. We trigger a logical view update
 * through external means, so that functions which are making several changes
 * don't have to flash the screen for every change. When the functions exit,
 * we'll fall back into this loop, and the update will be triggered. This
 * way it's only called once.
 */

#ifdef __STDC__
void main_loop(void)
#else
void main_loop ()
#endif
{
  XEvent event;
  Mailbox *mailbox;

  while(1) {
    XtAppNextEvent(context, &event);
    XtDispatchEvent(&event);

    for(mailbox = session->mailboxes; mailbox ; mailbox = mailbox->next) {
      if(mailbox->has_new_mail == TRUE) {
	get_new_message_info(mailbox);
	mailbox->has_new_mail = FALSE;
	if(preferences.newMailBeep == TRUE)
	  XBell(display,1000);
      }
      if(mailbox->flags_changed != 0L) {
	push_cursor(WATCH_CURSOR);
	XFlush(display);
	mail_check(mailbox->mailstream);
	pop_cursor();
	mailbox->flags_changed = 0L;
      }
    }
    if(session->update_needed) {
      update_views();
      session->update_needed = 0;
    }
  }
  /* NOTREACHED */
}


/*
 * Functions to capture the stderr stream from sub-processes
 * (or X window messages) and send them to the ML log window.
 */

#ifdef __STDC__
void redirect_stderr(XtPointer client_data, int *fd, XtInputId *id)
#else
void redirect_stderr(client_data, fd, id)
     XtPointer client_data;
     int *fd;
     XtInputId *id;
#endif
{
  char buf[FILEBUFFLEN];
  int nbytes;

  if((nbytes = read(*fd,buf,FILEBUFFLEN - 1)) == (EOF))
    return;
  buf[nbytes] = NUL_TERM;
  if(nbytes)
    log_error(buf, MLNOTIFY);
  return;
}


#ifdef __STDC__
void trap_stderr(void)
#else
void trap_stderr()
#endif
{
  (void) signal(SIGPIPE,SIG_IGN);

  if(pipe(errfd))
    return;

  dup2(errfd[1], fileno(stderr));
  close(errfd[1]);

  XtAppAddInput(context, errfd[0], (caddr_t) XtInputReadMask, 
		(XtInputCallbackProc) redirect_stderr, NULL);

  return;
}


#ifdef __STDC__
void setup_editres(Widget shell)
#else
void setup_editres(shell)
     Widget shell;
#endif
{
#ifndef NO_EDITRES
  XtAddEventHandler(shell, (EventMask) 0, 
		    True, _XEditResCheckMessages, NULL );
#endif
}


/*
 * Pops the window to the front. De-iconifies it if neccessary first.
 */

#ifdef __STDC__
void de_iconify(Widget shell)
#else
void de_iconify(shell)
     Widget shell;
#endif
{
  XMapWindow(display,XtWindow(shell));
  XRaiseWindow(display,XtWindow(shell));
}

#ifdef __STDC__
Mailbox *find_mailbox_from_mailstream(MAILSTREAM *mailstream)
#else
Mailbox *find_mailbox_from_mailstream(mailstream)
     MAILSTREAM *mailstream;
#endif
{
  Mailbox *mailbox;

  for(mailbox = session->mailboxes; mailbox ; mailbox = mailbox->next)
    if(mailbox->mailstream == mailstream)
      return(mailbox);
  return(NULL);
}



#ifdef __STDC__
void MLDestroyCallback (Widget w, XtPointer data, XtPointer cb)
#else
void MLDestroyCallback (w, data, cb)
Widget w;
XtPointer data;
XtPointer cb;
#endif
{
  Widget dismissWidget;

  if ((dismissWidget = XtNameToWidget (w, "*dismiss")) != NULL) {
    XtCallCallbacks (dismissWidget, XmNactivateCallback, NULL);
  }
  else 
    if ((dismissWidget = XtNameToWidget (w, "*cancel")) != NULL) {
      XtCallCallbacks (dismissWidget, XmNactivateCallback, NULL);
    }
    else 
      if ((dismissWidget = XtNameToWidget (w, "*close_window")) != NULL) {
        XtCallCallbacks (dismissWidget, XmNactivateCallback, NULL);
      }
      else 
	if ((dismissWidget = XtNameToWidget (w, "*exit")) != NULL) {
	  XtCallCallbacks (dismissWidget, XmNactivateCallback, NULL);
	}
  return;
}

#ifdef __STDC__
void AddDestroyCallback (Widget w)
#else
void AddDestroyCallback (w)
     Widget w;
#endif
{
  Atom wmDestroyWindow = (Atom) NULL;
  
  wmDestroyWindow = XmInternAtom (XtDisplay (w), "WM_DELETE_WINDOW", False);
  XmAddWMProtocolCallback (w, wmDestroyWindow, MLDestroyCallback, NULL);
  return;
}


#ifdef __STDC__
void MLSaveYourselfCallback (Widget w, XtPointer data, XtPointer cb)
#else
void MLSaveYourselfCallback (w, data, cb)
     Widget w;
     XtPointer data;
     XtPointer cb;
#endif
{
  XSetCommand(XtDisplay (w), XtWindow (w), Argv, Argc);
  return;
}
  
#ifdef __STDC__
void AddSaveYourselfCallback (Widget w)
#else
void AddSaveYourselfCallback (w)
     Widget w;
#endif
{
    Atom wmSaveYourself = (Atom) NULL;

    wmSaveYourself = XmInternAtom (XtDisplay (w), "WM_SAVE_YOURSELF", False);
    XmAddWMProtocolCallback (w, wmSaveYourself, MLSaveYourselfCallback,NULL);
}

#ifdef __STDC__ 
Boolean CheckConnection(void)
#else
Boolean CheckConnection()
#endif
{
  FILE *fp;
  int result;

  if((preferences.netCheckCommand != NULL) 
     && (*preferences.netCheckCommand != NUL_TERM)) {
    if((fp = popen(preferences.netCheckCommand,"r")) != NULL) {
      result = pclose(fp);
      if(result) {
	session->connected = FALSE;
	return(FALSE);
      }
    }
  }
  session->connected = TRUE;
  return(TRUE);

}


syntax highlighted by Code2HTML, v. 0.9.1