/* msgfncs.c */

#include "ml.h"



/* 
 * This takes a message header, extracts the subject, and builds a
 * "reply" subject out of it. It does these things after
 * looking for a beginning "re" string.
 * something              - add a "re" -> "Re: something"
 * Re: something          - Keep it.
 * Re[n]: something       - Junk the junk.
 * Re: Re[n]: something   - Junk the junk.
 *
 * Return string is allocated and must be free'd when no longer needed.
 */

#ifdef __STDC__
char *get_reply_subject(char *header)
#else
char *get_reply_subject(header)
     char *header;
#endif
{
  char *ret;
  char *subject;
  char *decode_subject;
  char *decode_result;
  char *first_text;
  char *ptr;

  if(header == NULL)
    return(cpystr(EMPTYSTR));

  subject = get_header_field_contents(HEADER_SUBJECT, header);

  if(! subject)
    return(cpystr(MLGetLocalized(XtNmsgNoReplySubject,MsgNoReplySubject)));

  decode_subject = (char *) fs_get(strlen(subject) + 1);
  *decode_subject = NUL_TERM;
  ptr = subject;

  while((decode_result = iso_decode(&ptr,FALSE)) != NULL) {
    strcat(decode_subject,decode_result);
    fs_give((void **) &decode_result);
  }

  fs_give((void **) &subject);
  first_text = first_nonwhite(decode_subject);

  if(*first_text == NUL_TERM) {
    fs_give((void **) &decode_subject);
    return(cpystr(MLGetLocalized(XtNmsgNoReplySubject,MsgNoReplySubject)));
  }
    
  ret = (char *) fs_get(strlen(first_text) + 32);

  /* Hack to catch non-conformists */

  if(strncasecmp(first_text,"re: re[",7) == STRMATCH)
    first_text += 4;

  if(strncasecmp(first_text,"re[",3) == STRMATCH) {
    ptr = strchr(first_text + 3,']');
    if(ptr) {
      ptr ++;
      if(*ptr == ':')
	ptr ++;
      sprintf(ret,"Re:%s",ptr);
    }
    else {
      sprintf(ret,"Re: %s",first_text);
    }
  }
  else {
    if(strncasecmp(first_text,"re:",3) == STRMATCH) {
      sprintf(ret,"Re:%s",first_text + 3);
    }
    else
      sprintf(ret,"Re: %s", first_text);
  }

  fs_give((void **) &decode_subject);

  return(ret);
}

#ifdef __STDC__
char *get_forward_subject(char *header)
#else
char *get_forward_subject(header)
     char *header;
#endif
{
  char *ret;
  char *subject;
  char *decode_result;
  char *ptr;
  char *buffer = NULL;
  if(header == NULL)
    return(cpystr(EMPTYSTR));

  subject = get_header_field_contents(HEADER_SUBJECT, header);

  if(! subject)
    subject = cpystr(EMPTYSTR);

  buffer = (char *) fs_get(strlen(subject) + 64);
  ret = (char *) fs_get(strlen(subject) + 64);
  *buffer = NUL_TERM;

  ptr = subject;

  while((decode_result = iso_decode(&ptr,FALSE)) != NULL) {
    strcat(buffer,decode_result);
    fs_give((void **) &decode_result);
  }

  sprintf(ret,MLGetLocalized(XtNstrForwardSubject,StrForwardSubject),buffer);

  fs_give((void **) &subject);
  fs_give((void **) &buffer);
  return(ret);

}

#ifdef __STDC__
char *get_short_forward_line(char *header, int count)
#else
char *get_short_forward_line(header, count)
     char *header;
     int count;
#endif
{
  char *ret;
  char *subject;
  char *from;
  char *ptr;
  char *decode_ret;
  char *decode_result;

  if(header == NULL)
    return(cpystr(EMPTYSTR));

  from = get_header_field_contents(HEADER_FROM, header);

  if(! from)
    from = cpystr(EMPTYSTR);

  subject = get_header_field_contents(HEADER_SUBJECT, header);

  if(! subject)
    subject = cpystr(EMPTYSTR);

  decode_ret = (char *) fs_get(strlen(subject) + 1);
  *decode_ret = NUL_TERM;

  ptr = subject;

  while((decode_result = iso_decode(&ptr,FALSE)) != NULL) {
    strcat(decode_ret,decode_result);
    fs_give((void **) &decode_result);
  }

  ret = (char *) fs_get(strlen(from) + strlen(subject) + 64);

  sprintf(ret,"\t%3d) %s\n\t\t[%-.64s]\n",count, from, decode_ret);

  fs_give((void **) &from);
  fs_give((void **) &subject);
  fs_give((void **) &decode_ret);

  return(ret);

}





#ifdef __STDC__
char *get_forward_text(char *text)
#else
char *get_forward_text(text)
     char *text;
#endif
{
  char *ret;

  if(text == NULL)
    return(cpystr(EMPTYSTR));

  ret = (char *) fs_get(strlen(text) + 256);

  strcpy(ret,MLGetLocalized(XtNstrBeginForward,StrBeginForward));
  strcat(ret,text);
  strcat(ret,MLGetLocalized(XtNstrEndForward,StrEndForward));

  return(ret);

}

#ifdef __STDC__
char *get_short_header(char *header)
#else
char *get_short_header(header)
     char *header;
#endif
{
  int cnt = 0;

  char *existing = NULL;
  char *new = NULL;
  char *tmp = NULL;

  if((session->header_set == (char **) NULL) || (header == NULL))
    return(cpystr(EMPTYSTR));

  for(cnt = 0; session->header_set[cnt] != NULL; cnt ++) {
    tmp = get_formatted_header_field(session->header_set[cnt], header);
    if(tmp == NULL)
      continue;
    if(existing) {
      new = (char *) fs_get(strlen(existing) + strlen(tmp) + 1);
      strcpy(new,existing);
      strcat(new,tmp);
      fs_give((void **) &existing);
      existing = new;
    }
    else
      existing = cpystr(tmp);
    fs_give((void **) &tmp);
  }

  return((existing) ? existing : cpystr(EMPTYSTR));
}


/*
 * Matches "field" (case-insensitive, colon OR no colon on input) in the
 * header, and returns the contents of the field, including continuation
 * lines, but without any LF's. The field name is not returned, only the 
 * contents.
 */

#ifdef __STDC__
char *get_header_field_contents(char *field, char *header)
#else
char *get_header_field_contents(field,header)
     char *field;
     char *header;
#endif
{
  char *ptr = header;
  unsigned long len   = strlen(field);
  unsigned long fieldlen = 0;
  Boolean match = FALSE;
  char *ret = NULL;

  if(! header)
    return(ret);

  while(*ptr) {
    if(((strncasecmp(ptr,field,len)) == STRMATCH) &&
       (((*(ptr + len)) == COLONCHAR) 
	|| ((*(ptr + (len - 1))) == COLONCHAR))) {
      match = TRUE;
      if((*(ptr + len)) == COLONCHAR)
	ptr += len + 1;
      else
	ptr += len;
      while(isspace(*ptr))
	    ptr ++;
      break;
    }
    else {
      while(*ptr) {
	if(*ptr == LFCHAR) {
	  ptr ++;
	  break;
	}
	ptr ++;
      }
    }
  }
  if(match == TRUE) {
    fieldlen = header_field_len(ptr);
    ret = (char *) fs_get(fieldlen + 1);
    strncpy(ret,ptr,fieldlen);
    ret[fieldlen] = NUL_TERM;
    striplf(ret);
    tabtospace(ret);
  }
  return(ret);
}

/* 
 * Finds a given field in the header (case insensitive, no colon on input),
 * and produces a suitable for display representation, with a pseudo tab
 * stop after the field name is printed, but this is ignored if the field
 * name is bigger than the tab length.
 * The field contents includes continuation lines and all linefeeds. the only
 * formatting done is the initial pseudo tab and expansion of encoded header
 * text if the charset is one that is useable.
 */ 

#ifdef __STDC__
char *get_formatted_header_field(char *field, char *header)
#else
char *get_formatted_header_field(field,header)
     char *field;
     char *header;
#endif
{
  char *ptr = header;
  unsigned long len   = strlen(field);
  unsigned long fieldlen = 0;
  Boolean match = FALSE;
  char *ret = NULL;
  char *firstwhite;
  char *newret;
  char *iso_out;

  if(! header)
    return(ret);

  while(*ptr) {
    if(((strncasecmp(ptr,field,len)) == STRMATCH) &&
       ((*(ptr + len)) == COLONCHAR)) {
      match = TRUE;
      break;
    }
    else {
      while(*ptr) {
	if(*ptr == LFCHAR) {
	  ptr ++;
	  break;
	}
	ptr ++;
      }
    }
  }

  if(match == TRUE) {
    fieldlen = header_field_len(ptr);
    ret = (char *) fs_get((2 * fieldlen) + (2 * HEADER_TAB_LEN));
    firstwhite = strchr(ptr,COLONCHAR);
    firstwhite ++;
    if((firstwhite - ptr) < HEADER_TAB_LEN) {
      strncpy(ret,ptr,firstwhite - ptr);
      ret[firstwhite - ptr] = NUL_TERM;
      strncat(ret,BLANK_STR,HEADER_TAB_LEN - (firstwhite - ptr));
      ret[HEADER_TAB_LEN] = NUL_TERM;
      strncat(ret,firstwhite,fieldlen - (firstwhite - ptr));
      ret[fieldlen + (HEADER_TAB_LEN - (firstwhite - ptr))] = NUL_TERM;
    }
    else {
      strncpy(ret,ptr,fieldlen);
      ret[fieldlen] = NUL_TERM;
    }
    ptr = ret;
    newret = (char *) fs_get(strlen(ret) + 1);
    *newret = NUL_TERM;
    while((iso_out = iso_decode(&ptr, TRUE)) != NULL) {
      strcat(newret,iso_out);
      fs_give((void **) &iso_out);
    }
    fs_give((void **) &ret);
    ret = newret;
  }
  return(ret);
}



/* 
 * Assumes that str points to the beginning of a header field.
 * It finds the end of the field (allowing for continuation lines)
 * and returns the length. This is used by the functions to extract
 * particular header fields.
 */


#ifdef __STDC__
unsigned long header_field_len(char *str)
#else
unsigned long header_field_len(str)
  char *str;
#endif
{
  unsigned long n = 0;
  char *ptr;

  for(ptr = str; *ptr ; ptr ++, n ++) {
    if(*ptr == LFCHAR) {
      if((*(ptr + 1) == TABCHAR) || (*(ptr + 1) == SPACECHAR))
	continue;
      else
	break;
    }
  }
  return(n + 1);

}

#ifdef __STDC__ 
void elt_to_tm(MESSAGECACHE *elt, struct tm *Tm)
#else
void elt_to_tm(elt, Tm)
     MESSAGECACHE *elt;
     struct tm *Tm;
#endif
{
  if(elt) {
    memset(Tm, 0, sizeof(struct tm));
    Tm->tm_year = (elt->year + BASEYEAR) - 1900;
    Tm->tm_mon = elt->month - 1;
    Tm->tm_mday = elt->day;
    Tm->tm_hour = elt->hours;
    Tm->tm_min = elt->minutes;
    Tm->tm_sec = elt->seconds;
    Tm->tm_isdst = (-1);   /* Unavailable */
  }
  return;
}


#ifdef __STDC__
char *ml_fetchfrom(Message *message)
#else
char *ml_fetchfrom(message)
     Message *message;
#endif
{
  ADDRESS *address;
  char buffer[FILEBUFFLEN];
  char *ptr;
  char *decode_ret;
  *buffer = NUL_TERM;

  if(message->envelope->from != NULL) {
    address = message->envelope->from;
    while((address != NULL) && (address->host == NULL))
      address = address->next;
    if(address != NULL) {
      if(address->personal == NULL)
	sprintf(buffer,"%s@%s",address->mailbox,address->host);
      else {
	ptr = address->personal;
	while((decode_ret = iso_decode(&ptr,FALSE)) != NULL) {
	  strcat(buffer,decode_ret);
	  fs_give((void **) &decode_ret);
	}
      }
    }
  }
  return(cpystr(buffer));
}


/*
 * make_view_line is now a wrapper for format_line
 */

#ifdef __STDC__
XmString make_view_line(Message *message)
#else
XmString make_view_line(message)
     Message *message;
#endif
{
  char buffer[FILEBUFFLEN];

  format_line(message, buffer, preferences.viewline_format);
  return(XmStringCreateSimple(buffer));
}


#ifdef __STDC__
void make_attribution_line(Message *message, char *buffer)
#else
void make_attribution_line(message,buffer)
     Message *message;
     char *buffer;
#endif
{
  *buffer = '\0';
  if (*preferences.attribution_format) {
    format_line(message, buffer, preferences.attribution_format);
  }
}


/*
 * the original make_view_line function, genericised
 * to get the format string as an argument
 */

#ifdef __STDC__
void format_line(Message *message, char *buffer, char *fmt)
#else
void format_line(message,buffer,fmt)
     Message *message;
     char *buffer;
     char *fmt;
#endif
{
  char mnum[32];
  char date[256];
  char size[32];
  char flags[32];
  char *type;
  char *subject;
  char *fix_subject;
  char fmttmp[FILEBUFFLEN], raddrtmp[FILEBUFFLEN], saddrtmp[FILEBUFFLEN];
  char *vln, *ref;
  char *rpersonal, *ruser, *rhost, *spersonal, *suser, *shost;
  int tomyself;
  struct tm t;

  MESSAGECACHE *elt;
  XmString ret;

  /* We have the info ?? */

  if(message && message->longcache && message->envelope) {

    elt = &(message->longcache->elt);
    
    sprintf(mnum,"%lu",message->msgno);

    flags[0] = 
      (elt->recent) 
	? ((elt->seen) ? 'R' : 'N'):
	  ((elt->seen) ? ' ' : 'U');
    flags[1] = (elt->flagged)  ? 'F' : ' ';
    flags[2] = (elt->answered) ? 'A' : ' ';
    flags[3] = (elt->deleted)  ? 'D' : ' ';
    flags[4] = NUL_TERM;
    
    if(message->sender == NULL) 
      message->sender = ml_fetchfrom(message);
    
    if(message->body) {
      if(message->body->type == TYPETEXT)
	type = "T";
      else
	type = "M";
    }
    else 
      type = " ";

    if(message->subject == NULL) {
      subject = message->envelope->subject;
      if(subject == NULL)
	subject = EMPTYSTR;
      message->subject = (char *) fs_get(strlen(subject) + 1);
      *message->subject = NUL_TERM;
      while((fix_subject = iso_decode(&subject,FALSE)) != NULL) {
	strcat(message->subject,fix_subject);
	fs_give((void **) &fix_subject);
      }
    }

    /* View line format components
       %D{format} define date format (for strftime)
       %S subject
       %n message number
       %F flags
       %T type
       %L length
       %P sender, personal name (if not known, substituted with %U@%H)
       %U sender, user name
       %H sender, host
       %A sender, user@host
       %p receiver, personal name (if not known, substituted with %u@%h)
       %u receiver, user name
       %h receiver, host
       %a receiver, user@host
       %M{sss} if sender matches current login name, use %p for %P, %u for %U,
       %h for %H, %a for %A, and insert sss
       %N{sss} if sender do not match current login name, insert sss
       */
    
    rpersonal = (message->envelope && message->envelope->to
		 && message->envelope->to->personal) ?
		   message->envelope->to->personal : NULL;
    ruser = (message->envelope && message->envelope->to
	     && message->envelope->to->mailbox) ?
	       message->envelope->to->mailbox : "";
    rhost = (message->envelope && message->envelope->to
	     && message->envelope->to->host) ?
	       message->envelope->to->host : "";
    spersonal = message->sender;
    shost = (message->envelope && message->envelope->from
	     && message->envelope->from->host) ?
	       message->envelope->from->host : "";
    suser = (message->envelope && message->envelope->from
	     && message->envelope->from->mailbox) ?
	       message->envelope->from->mailbox : "";
    sprintf(raddrtmp, "%s@%s", ruser, rhost);
    sprintf(saddrtmp, "%s@%s", suser, shost);
    if (!rpersonal)
      rpersonal = raddrtmp;
    if (!spersonal)
      spersonal = saddrtmp;
    tomyself = (message->sender && local_auth.fullname
		&& (strcmp(message->sender, local_auth.fullname) == 0));
    *buffer = '\0'; vln = buffer;

    for (; *fmt;) {
      char* ffmt = fmttmp;
      if (*fmt == '%') {
	if (*(fmt+2) == '{') {
	  char f = *(fmt+1);
	  fmt += 3;
	  while (*fmt && (*fmt != '}'))
	    *ffmt++ = *fmt++;
	  *ffmt++ = '\0';
	  fmt++;
	  switch (f) {
	  case 'M':
	    if (tomyself) {
	      spersonal = rpersonal;
	      suser = ruser;
	      shost = rhost;
	      strcpy(saddrtmp, raddrtmp);
	    }
	    else
	      *fmttmp = '\0';
	    break;
	  case 'N':
	    if (tomyself)
	      *fmttmp = '\0';
	    break;
	  case 'D':
	    elt_to_tm(elt,&t);
	    strftime(date,sizeof(date),fmttmp,&t);
	    strcpy(fmttmp, date);
	    break;
	  default:
	    break;
	  }
	}
	else {
	  *ffmt++ = *fmt++;
	  while (((*fmt >= '0') && (*fmt < '9'))
		 || (*fmt == '-') || (*fmt == '.'))
	    *ffmt++ = *fmt++;
	  *ffmt++ = 's';
	  *ffmt = '\0';
	  switch (*fmt++) {
	  case 'D': ref = "Invalid use of %D flag"; break;
	  case 'n': ref = mnum; break;
	  case 'S': ref = message->subject; break;
	  case 'F': ref = flags; break;
	  case 'T': ref = type; break;
	  case 'L':
	    if(elt->rfc822_size > (1000000L * 1024L))
	      sprintf(size,"%luG",elt->rfc822_size / (1000000L * 1024L));
	    else
	      if(elt->rfc822_size > (1000L * 1024L))
		sprintf(size,"%luM)",elt->rfc822_size / (1000L * 1024L));
	      else
		if(elt->rfc822_size > (9L * 1024L))
		  sprintf(size,"%luK",elt->rfc822_size / (1024L));
		else
		  sprintf(size,"%lu",elt->rfc822_size);
	    ref = size;
	    break;
	  case 'P': ref = spersonal; break;
	  case 'U': ref = suser; break;
	  case 'H': ref = shost; break;
	  case 'A': ref = saddrtmp; break;
	  case 'p': ref = rpersonal; break;
	  case 'u': ref = ruser; break;
	  case 'h': ref = rhost; break;
	  case 'a': ref = raddrtmp; break;
	  case 'M': ref = "Invalid use of %M flag"; break;
	  case '%': ref = "%"; break;
	  default:
	    ref = "<Unknown flag>";
	  }
	}
	sprintf(vln, fmttmp, ref);
	vln += strlen(vln);
      }
      else
	*vln++ = *fmt++;
      
    }
     
  }
  else     /* We don't have the info */
    sprintf(buffer,MLGetLocalized(XtNmsgNotYetLoaded,MsgNotYetLoaded));
}


#ifdef __STDC__
char *byte_print(unsigned long size, char *buffer)
#else
char *byte_print(size, buffer)
     unsigned long size;
     char *buffer;
#endif
{
  if(size > (1000000L * 1024L))
    sprintf(buffer,"(%3luG)", size / (1000000L * 1024L));
  else
    if(size > (1000L * 1024L))
      sprintf(buffer,"(%3luM)", size / (1000L * 1024L));
    else
      if(size > (9L * 1024L))
	sprintf(buffer,"(%3luK)", size / (1024L));
      else
	sprintf(buffer,"(%4lu)", size);
  
  return(buffer);
}


#ifdef __STDC__
void fast_fetch_message_info(Mailbox *mailbox, Lview *lview)
#else
void fast_fetch_message_info(mailbox,lview)
     Mailbox *mailbox;
     Lview *lview;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  MainWindow *mwin = find_main_window(mailbox);
  unsigned long prefetch;
  unsigned long start;
  unsigned long x;
  Message *message;
  Message_List *message_list;
  Message_List *prev = NULL;
  double percentage = 0.0;
  int int_percent = 0;
  int old_percent = 0;
  char log[FILEBUFFLEN];
  Boolean fetchnomore = FALSE;
  MESSAGECACHE *elt;
  unsigned long len;


  if(XtIsRealized(mwin->shell)) {
    de_iconify(mwin->shell);
    XmUpdateDisplay(mwin->shell);
  }

  if(mailbox->nmsgs == 0 || lview->count == 0) {
    mailbox->fetched = TRUE;
    mailbox->bg_timer = (XtIntervalId) 0;
    return;
  }

  if(mailbox->type == MAILBOX_TYPE_NEWS)
    prefetch = (unsigned long) preferences.news_prefetch;
  else
    prefetch = (unsigned long) preferences.mail_prefetch;


  if(prefetch == 0L)
    start = 1L;
  else {
    if(lview->count < prefetch)
      start = 1L;
    else {
      start = lview->count - prefetch;
      if(start < 1L)
	start = 1L;
      sprintf(log,MLGetLocalized(XtNmsgBackgrounding,MsgBackgrounding),start);
      mm_log(log,MLNOTIFY);
    }
  }

  mailbox->first_fetch = start;

  /* Create the message list and populate with message structures. */

  for(x = 1; x <= lview->count; x ++ ) {

    message_list = new_message_list();

    if(prev) {
      prev->next = message_list;
      message_list->prev = prev;
    }
    else
      lview->message_list = message_list;
    
    message_list->number = x;
    message = new_message();
    message_list->message = message;
    message->msgno = x;
    message->mailbox = mailbox;
    message->mailstream = mailbox->mailstream;
    prev = message_list;
    
  }

  /* Now fetch the messages. */

  if(mailbox->type == MAILBOX_TYPE_MAIL) {
  
    message_list = lview->message_list;

    for(x = 1; x <= lview->count; x ++) {
      message = message_list->message;
      
      percentage = ((double) x / (double) lview->count) * 100.0 ;
      int_percent = (int) percentage;
      if((int_percent > 0) 
	 && (int_percent <= 100) 
	 && (int_percent != old_percent)) {
	XtVaSetValues(mwin->slider, XmNvalue, int_percent, NULL);
	XmUpdateDisplay(mwin->shell);
	old_percent = int_percent;
      }
      
      
      if(x >= start) {
	message->envelope = mail_fetchstructure(message->mailstream,
						message->msgno,
						&message->body);
	if(message->envelope == NULL)
	  continue;
	message->longcache = mail_lelt(message->mailstream, message->msgno);
	message->fetched  = TRUE;
	if(message->envelope->in_reply_to != NULL)
	  message->references = cpystr(message->envelope->in_reply_to);
	else
	  message->references = cpystr(EMPTYSTR);
	fix_references_field(&message->references);
	if(message->envelope->message_id != NULL)
	  message->message_id = cpystr(message->envelope->message_id);
	else
	  message->message_id = cpystr(EMPTYSTR);
      }
      message->viewline = make_view_line(message);
      if(message_list->next)
	message_list = message_list->next;
    }
  }
  else {
    
    /* news. Go backwards until we find a deleted entry */
    
    message_list = prev; /* already set to the end for us */
    
    for(x = lview->count; x >= 1; x --) {
      
      message = message_list->message;
      
      percentage = 
	((double) (lview->count - x) / (double) lview->count) * 100.0 ;
      int_percent = (int) percentage;
      if((int_percent > 0) 
	 && (int_percent <= 100) 
	 && (int_percent != old_percent)) {
	XtVaSetValues(mwin->slider, XmNvalue, int_percent, NULL);
	XmUpdateDisplay(mwin->shell);
	old_percent = int_percent;
      }
      
      if((x >= start) && (fetchnomore == FALSE)) {
	message->envelope = mail_fetchstructure(message->mailstream,
						message->msgno,
						&message->body);
	if(message->envelope == NULL)
	  continue;
	message->longcache = mail_lelt(message->mailstream, message->msgno);
	message->fetched  = TRUE;
	elt = &(message->longcache->elt);
	if(elt->deleted) {
	  fetchnomore = TRUE;
	  if(message->msgno > 1) 
	    mailbox->first_fetch = start = 1L;
	}

	if(message->envelope->in_reply_to != NULL)
	  message->references = cpystr(message->envelope->in_reply_to);
	else 
	  message->references = cpystr(EMPTYSTR);
	fix_references_field(&message->references);
	if(message->envelope->message_id != NULL)
	  message->message_id = cpystr(message->envelope->message_id);
	else
	  message->message_id = cpystr(EMPTYSTR);
      }
      message->viewline = make_view_line(message);

      if(fetchnomore == TRUE) {
	lview->pruned = x;
	lview->count = lview->count - lview->pruned;
	lview->prune_happened = TRUE;
	prev = message_list->next;
	message_list->next = NULL;
	free_message_list(lview->message_list,TRUE,TRUE);
	lview->message_list = prev;
	mm_log(MLGetLocalized(XtNmsgPruning,MsgPruning), NIL);
	break;
      }
      if(message_list->prev) 
	message_list = message_list->prev;
    }
  }

  /* make sure we get that last little bit in the scrollbar */


  XtVaSetValues(mwin->slider, XmNvalue, 100, NULL);
  XmUpdateDisplay(mwin->shell);

  mailbox->fetched = TRUE;
  if((start != 1L) && (fetchnomore == FALSE)) {
    mailbox->bg_timer = XtAppAddTimeOut(context,
					(mailbox->type == MAILBOX_TYPE_NEWS) 
					? (preferences.news_background * 1000)
					: (preferences.mail_background * 1000),
					(XtTimerCallbackProc) background_fetch,
					mailbox);
    if(mailbox->type == MAILBOX_TYPE_MAIL)
      mail_parameters(mailbox->mailstream,
		      SET_LOOKAHEAD,
		      (void *) preferences.mail_fetch);
  }
  else
    mailbox->bg_timer = (XtIntervalId) 0;

  if(XtIsRealized(mwin->shell)) {
    de_iconify(mwin->shell);
    XmUpdateDisplay(mwin->shell);
  }
  return;
}

#ifdef __STDC__
void get_new_message_info(Mailbox *mailbox)
#else
void get_new_message_info(mailbox)
     Mailbox *mailbox;
#endif
{
  FILE *fp;
  MainWindow *mwin;
  MESSAGECACHE *elt;
  int n = 0;
  char *ptr;
  Lview *lview;
  Message *message;
  Message_List *message_list;
  Message_List *prev = NULL;
  Message_List *new;
  char *fixedname;
  char buffer[FILEBUFFLEN];
  unsigned long len;

  mwin = find_main_window(mailbox);

  lview = get_all_lview(mailbox->mailstream);

  if(mwin == NULL || mailbox->nmsgs == 0 || lview == NULL)
    return;

  if(lview->message_list) {
    for(prev = lview->message_list; prev->next; prev = prev->next)
      ;
  }

  for(n = lview->pruned + lview->count + 1; n <= mailbox->nmsgs; n ++) {
    message_list = new_message_list();
    if(prev == NULL)
      lview->message_list = message_list;
    else
      prev->next = message_list;

    message_list->prev = prev;
    message_list->number = n;
    message = new_message();
    message_list->message = message;
    message->msgno = n;
    message->mailbox = mailbox;
    message->mailstream = mailbox->mailstream;
    message->envelope = mail_fetchstructure(message->mailstream,
					    message->msgno,
					    &message->body);
    message->longcache = mail_lelt(message->mailstream, message->msgno);
    if(message->envelope->in_reply_to != NULL)
      message->references = cpystr(message->envelope->in_reply_to);
    else 
      message->references = cpystr(EMPTYSTR);
    fix_references_field(&message->references);
    if(message->envelope->message_id != NULL)
      message->message_id = cpystr(message->envelope->message_id);
    else
      message->message_id = cpystr(EMPTYSTR);

    message->viewline = make_view_line(message);
    message->fetched  = TRUE;
    prev = message_list;

    if((preferences.newMailProg != NULL) 
       && (*preferences.newMailProg != NUL_TERM)) {
      sprintf(MLSender,"MLSENDER=%s", 
	      (message->sender) ? message->sender : EMPTYSTR);
      for(ptr = MLSender; *ptr; ptr ++) 
	if((*ptr == SPACECHAR) || (*ptr == TABCHAR) || (*ptr == DQUOTECHAR))
	  *ptr = '_';
      putenv(MLSender);
      fixedname = unfix_mailboxpath(message->mailbox->mailboxname);
      sprintf(MLMailbox,"MLMAILBOX=%s%s%s",
	      (message->mailbox->host) ? message->mailbox->host : EMPTYSTR,
	      (message->mailbox->host) ? ":" : EMPTYSTR,
	      fixedname);
      fs_give((void **) &fixedname);
      for(ptr = MLMailbox; *ptr; ptr ++) 
	if((*ptr == SPACECHAR) || (*ptr == TABCHAR) || (*ptr == DQUOTECHAR))
	  *ptr = '_';
      putenv(MLMailbox);
      fp = popen(preferences.newMailProg,"w");
      pclose(fp);
    }
    sprintf(buffer,MLGetLocalized(XtNmsgNewMessageFrom,MsgNewMessageFrom),
	    mailbox->imapname,
	    (message->sender) ? message->sender : EMPTYSTR,
	    (message->subject) ? message->subject : EMPTYSTR);
    mm_log(buffer, (preferences.alert_new) ? MLNOTIFY : NIL);
  }
  
  new = sort_message_list(lview->message_list, lview->representation);
  lview->message_list = new;
  lview->count = mailbox->nmsgs - lview->pruned;

  if((has_new_messages(lview,FALSE)) == TRUE)
    lview->has_new = TRUE;
  else
    lview->has_new = FALSE;

  if(mwin->current == lview)
    view_context_switch(mwin,lview,lview);

  update_logical_views(mailbox,FALSE);
  return;
}


#ifdef __STDC__
void fix_references_field(char **s)
#else
void fix_references_field(s)
     char **s;
#endif
{
  char *start;
  char *end;
  char *ret;

  if(*s == NULL) {
    *s = cpystr(EMPTYSTR);
    return;
  }
  if(((start = strchr(*s,'<')) != NULL) 
     && ((end = strchr(start,'>')) != NULL)){
    *(end + 1) = NUL_TERM;
    ret = cpystr(start);
  }
  else
    ret = cpystr(EMPTYSTR);

  free(*s);
  *s = ret;
  return;
}






#ifdef __STDC__
Boolean background_fetch_message(Mailbox *mailbox, unsigned long msgno, 
				 Boolean recurse)
#else
Boolean background_fetch_message(mailbox,msgno, recurse)
     Mailbox *mailbox;
     unsigned long msgno;
     Boolean recurse;
#endif
{
  Message *message;
  MESSAGECACHE *elt;
  int i;
  unsigned long len;


  message = get_message_from_mailbox(mailbox,msgno);
  if((message == NULL) || (message->fetched == TRUE))
    return(FALSE);


  message->envelope = mail_fetchstructure(message->mailstream,
					  message->msgno,
					  &message->body);
  message->longcache = mail_lelt(message->mailstream, message->msgno);
  message->fetched  = TRUE;

  if(message->envelope->in_reply_to != NULL)
    message->references = cpystr(message->envelope->in_reply_to);
  else
    message->references = cpystr(EMPTYSTR);
  fix_references_field(&message->references);
  if(message->envelope->message_id != NULL)
    message->message_id = cpystr(message->envelope->message_id);
  else
    message->message_id = cpystr(EMPTYSTR);

  update_view_line(mailbox,message);

  if((mailbox->type == MAILBOX_TYPE_NEWS) && (recurse == FALSE)) {
    elt = &(message->longcache->elt);
    if(elt->deleted) {
      /* get the rest of this fetch sequence */
      for(i = message->msgno + 1; 
	  ((background_fetch_message(mailbox,i, TRUE)) == TRUE); i ++)
	;
      prune_mailbox(mailbox,message->msgno);
      return(FALSE);
    }
  }
  return(TRUE);
}

#ifdef __STDC__
void prune_mailbox(Mailbox *mailbox, unsigned long msgno)
#else
void prune_mailbox(mailbox,msgno)
     Mailbox *mailbox;
     unsigned long msgno;
#endif
{
  Lview *lview;
  Message_List *message_list;
  Message_List *next = NULL;
  Message_List *deleted;
  MESSAGECACHE *elt;
  Message_List *new;
  unsigned long n = 0L;

  lview = get_all_lview(mailbox->mailstream);
  if(lview) {

    new = sort_message_list(lview->message_list, REP_ASCENDING);

    for(message_list = new; 
	(message_list 
	&& message_list->message 
	&& message_list->message->msgno != msgno); 
	message_list = message_list->next)
      ;
    deleted = message_list;

    for(message_list = message_list; 
	message_list; message_list = message_list->next) {
      if(message_list->message && message_list->message->longcache) {
	elt = &(message_list->message->longcache->elt);
	if(elt && elt->deleted)
	  deleted = message_list;
	else
	  break;
      }
    }

    if(deleted && deleted->message) {
      next = deleted->next;
      deleted->next = NULL;
      free_message_list(new ,TRUE, TRUE);
      lview->message_list = next;
      lview->pruned = deleted->message->msgno;
      lview->count = lview->count - lview->pruned;
      mailbox->first_fetch = 1L;
    }

    lview->message_list = sort_message_list(lview->message_list,
					    lview->representation);
  }

  mm_log(MLGetLocalized(XtNmsgPruning,MsgPruning), NIL);
  return;
}

/* Called from the mm_flags() callback */

#ifdef __STDC__
void update_view_line_stream_msgno(MAILSTREAM *mailstream, unsigned long msgno)
#else
void update_view_line_stream_msgno(mailstream,msgno)
     MAILSTREAM *mailstream;
     unsigned long msgno;
#endif
{
  Message *message;
  Mailbox *mailbox;

  mailbox = find_mailbox_from_mailstream(mailstream);
  
  if(mailbox && mailbox->fetched) {
    message = get_message_from_mailbox(mailbox,msgno);
    mailbox->flags_changed ++;
    if(message)
      update_view_line(mailbox,message);
  }
  return;
}	

#ifdef __STDC__
void update_view_line(Mailbox *mailbox, Message *message)
#else
void update_view_line(mailbox,message)
     Mailbox *mailbox;
     Message *message;
#endif
{

  MainWindow *mwin;
  Message_List *message_list;
  XmString xstr = NULL;
  Boolean different = FALSE;

  mwin = find_main_window(mailbox);

  if(mwin == NULL)
    return;

  xstr = make_view_line(message);

  if(xstr == NULL)
    return;
  
  /* we only need to update things if the string has actually changed. */
  if((message->viewline != NULL)
     && ((XmStringCompare(xstr,message->viewline)) == FALSE))
    different = TRUE;
  
  if(message->viewline == NULL) /* just in case wasn't set already */
    different = TRUE;
  
  if(different == TRUE) {       /* Set the new structure value */
    if(message->viewline)
      XmStringFree(message->viewline);
    message->viewline = xstr;
  }
  else {                       /* No changes needed. Go home. */
    XmStringFree(xstr);
    return;
  }
  
  /* 
   * If the current view contains the message, update it.
   * Preserve highlight. If not in the view, we've already changed
   * the structure value, and it will take effect on the next
   * context switch. One of the reasons for checking "sameness" above
   * is that highlighting gets messed up if we re-highlight the same
   * string value, which toggles it off and doesn't tell us.
   */

  if((mwin->current != NULL)
     && (mwin->current->mailstream == message->mailstream)) {
    for(message_list = mwin->current->message_list; 
	message_list; message_list = message_list->next) {
      if(message_list->message == message) {
	mailbox->refresh_needed ++;
	break;
      }
    }
  }

  mailbox->update_needed ++;
  session->update_needed ++;

  return;
}

#ifdef __STDC__
Message *get_message_from_mailbox(Mailbox *mailbox, unsigned long msgno)
#else
Message *get_message_from_mailbox(mailbox,msgno)
     Mailbox *mailbox;
     unsigned long msgno;
#endif
{
  Lview *lview;
  Message_List *message_list;

  lview = get_all_lview(mailbox->mailstream);

  if(lview) {
    for(message_list = lview->message_list;
	message_list; message_list = message_list->next) 
      if((message_list->message) && (message_list->message->msgno == msgno))
	return(message_list->message);
  }

  return(NULL);
}


/* 
 * Looks at the current highlighted messages and generates an IMAP
 * sequence string. This can be individual messages "n,nn,nnn,[etc.]" 
 * and/or message ranges denoted by "start_n:end_n". The range notation
 * cuts down considerable network bandwidth, but won't work well, (if at all)
 * if we have an alternate representation, like reverse order or sorted views.
 * We don't even try to optimize those. Hopefully the c-client won't crash
 * if we feed it an entire selected mailbox in reverse order.
 */

#ifdef __STDC__
char *generate_sequence(Lview *lview)
#else
char *generate_sequence(lview)
     Lview *lview;
#endif
{
    Message_List *message_list;
    char *buffer;
    char *sequence;
    unsigned long found = 0L;
    unsigned long range_begin = 0L;
    unsigned long last_range = 0L;
    Boolean range = FALSE;
    

    if(lview == NULL || lview->num_selected == 0L)
      return(NULL);

    /*
     * Overkill (worst case) allocation. 9 million messages out of
     * sequence starting at 1000000 (plus a separator char) is unlikely.
     * The program will have crashed elsewhere or would have hit system
     * limits if somebody ever overflows this buffer.
     */

    buffer = (char *) fs_get(8 * lview->num_selected);
    sequence = buffer;
    *sequence = NUL_TERM;		

    for(message_list = lview->message_list; message_list; 
	message_list = message_list->next) {
      if(message_list->selected == FALSE)
	continue;

      /* 
       * if we're in a range sequence and haven't reached the end of it,
       * update the last_range variable and keep going
       */

      if((found != 0) 
	 && (range == TRUE) 
	 && (message_list->next != NULL) 
	 && (message_list->next->selected == TRUE)
	 && (message_list->message)
	 && (message_list->next->message)
	 && (message_list->next->message->msgno == (last_range +1))) {
	last_range = message_list->next->message->msgno;
	found ++;
	continue;
      }

      /* 
       * See if this qualifies as a range sequence.
       */

      if((range == FALSE)
	 && (message_list->next != NULL)
	 && (message_list->next->selected == TRUE)
	 && (message_list->message)
	 && (message_list->next->message)
	 && (message_list->next->message->msgno 
	     == (message_list->message->msgno + 1))) {
	range = TRUE;
	range_begin = message_list->message->msgno;
	last_range = message_list->next->message->msgno;
	if(found != 0) {
	  strcat(sequence,",");
	  sequence += 1;
	}
	sprintf(sequence,"%ld", message_list->message->msgno);
	sequence += strlen(sequence);
	found ++;
	continue;
      }

      /* We've found the last in a range. Flush it. */

      if((range == TRUE) 
	 && (range_begin != 0)) {
	sprintf(sequence,":%lu",last_range);
	range = FALSE;
	range_begin = 0L;
	last_range = 0L;
	found ++;
	sequence += strlen(sequence);
	continue;
      }

      /* 
       * Not a range. If the first message, start the string.
       * Otherwise, add a comma, then the current number.
       */
	
      if(found == 0)  
	sprintf(sequence,"%ld", message_list->message->msgno);
      else
	sprintf(sequence,",%ld", message_list->message->msgno);
      found ++;
      sequence += strlen(sequence);
    }

    return(buffer);			
}



/* 
 * This is the same as the above function except that it builds the list
 * from the current lview, ignoring highlight state. Every message in the 
 * lview becomes part of the sequence.
 */

#ifdef __STDC__
char *generate_sequence_unselected(Lview *lview)
#else
char *generate_sequence_unselected(lview)
     Lview *lview;
#endif
{
    Message_List *message_list;
    char *buffer;
    char *sequence;
    unsigned long found = 0L;
    unsigned long range_begin = 0L;
    unsigned long last_range = 0L;
    Boolean range = FALSE;
    

    if(lview == NULL || lview->count == 0L)
      return(NULL);

    /*
     * Overkill (worst case) allocation. 9 million messages out of
     * sequence starting at 1000000 (plus a separator char) is unlikely.
     * The program will have crashed elsewhere or would have hit system
     * limits if somebody ever overflows this buffer.
     */

    buffer = (char *) fs_get(8 * lview->count);
    sequence = buffer;
    *sequence = NUL_TERM;		

    for(message_list = lview->message_list; message_list; 
	message_list = message_list->next) {

      /* 
       * if we're in a range sequence and haven't reached the end of it,
       * update the last_range variable and keep going
       */

      if((found != 0) 
	 && (range == TRUE) 
	 && (message_list->next != NULL) 
	 && (message_list->message)
	 && (message_list->next->message)
	 && (message_list->next->message->msgno == (last_range +1))) {
	last_range = message_list->next->message->msgno;
	found ++;
	continue;
      }

      /* 
       * See if this qualifies as a range sequence.
       */

      if((range == FALSE)
	 && (message_list->next != NULL)
	 && (message_list->message)
	 && (message_list->next->message)
	 && (message_list->next->message->msgno 
	     == (message_list->message->msgno + 1))) {
	range = TRUE;
	range_begin = message_list->message->msgno;
	last_range = message_list->next->message->msgno;
	if(found != 0) {
	  strcat(sequence,",");
	  sequence += 1;
	}
	sprintf(sequence,"%ld", message_list->message->msgno);
	sequence += strlen(sequence);
	found ++;
	continue;
      }

      /* We've found the last in a range. Flush it. */

      if((range == TRUE) 
	 && (range_begin != 0)) {
	sprintf(sequence,":%lu",last_range);
	range = FALSE;
	range_begin = 0L;
	last_range = 0L;
	found ++;
	sequence += strlen(sequence);
	continue;
      }

      /* 
       * Not a range. If the first message, start the string.
       * Otherwise, add a comma, then the current number.
       */
	
      if(found == 0)  
	sprintf(sequence,"%ld", message_list->message->msgno);
      else
	sprintf(sequence,",%ld", message_list->message->msgno);
      found ++;
      sequence += strlen(sequence);
    }

    return(buffer);			
}



/* 
 * A partial misnomer. The "asking" is done during the file select.
 * The routine just saves a binary buffer or returns.
 */

#ifdef __STDC__
void ask_save(Widget w, Binary_Buffer *binary_buffer)
#else
void ask_save(w,binary_buffer)
     Widget w;
     Binary_Buffer *binary_buffer;
#endif
{
  FILE *fp;
  Boolean append_it;
  char *filename;
  int errors = 0;

  filename = file_select(w, NULL, NULL, NULL, TRUE, &append_it);
  if(filename == NULL)
    return;

  push_cursor(WATCH_CURSOR);
  if((fp = fopen(filename,(append_it == TRUE) ? "a" : "w")) == NULL)
    errors ++;

  if(! errors) { 
    if(fwrite(binary_buffer->data,binary_buffer->length,1,fp) != 1)
      errors ++;
    if(fclose(fp))
      errors ++;
  }

  if(errors)
    mm_log(MLGetLocalized(XtNmsgSaveFail,MsgSaveFail), WARN);
  else
    mm_log(MLGetLocalized(XtNmsgSaveSuccess,MsgSaveSuccess), NIL);

  fs_give((void **) &filename);

  pop_cursor();

  return;
}



#ifdef __STDC__
int copy_message_to_mailbox(MAILSTREAM *mailstream, unsigned long msgno,
			    char *dest)
#else
int copy_message_to_mailbox(mailstream,msgno,dest)
     MAILSTREAM *mailstream;
     unsigned long msgno;
     char *dest;
#endif
{
  extern STRINGDRIVER mail_string;
  STRING bs;
  int result = SYSCALL_SUCCESS;
  char *data1 = NULL;
  char *data2 = NULL;
  char *everything = NULL;

  if((mailstream == NULL) || (msgno == 0L) 
     || (dest == NULL) || (*dest == NUL_TERM))
    return(SYSCALL_FAILURE);

  data1 = cpystr(mail_fetchheader(mailstream,msgno));
  data2 = cpystr(mail_fetchtext(mailstream,msgno));

  if((data1 == NULL) && (data2 == NULL))
    return(SYSCALL_FAILURE);

  everything = (char *) fs_get(strlen(data1) + strlen(data2) + 1);
  strcpy(everything,data1);
  strcat(everything,data2);

  fs_give((void **) &data1);
  fs_give((void **) &data2);

  INIT(&bs, mail_string,everything,strlen(everything));
  if((mail_append_full(NIL,dest,NIL,NIL, &bs)) == NIL)
    result = SYSCALL_FAILURE;

  fs_give((void **) &everything);
  return(result);
}


#ifdef __STDC__
int copy_message_to_cachemailbox(MAILSTREAM *mailstream, unsigned long msgno,
			    char *dest)
#else
int copy_message_to_cachemailbox(mailstream,msgno,dest)
     MAILSTREAM *mailstream;
     unsigned long msgno;
     char *dest;
#endif
{
  extern STRINGDRIVER mail_string;
  STRING bs;
  int result = SYSCALL_SUCCESS;
  char *data1 = NULL;
  char *data2 = NULL;
  char *everything = NULL;

  if((mailstream == NULL) || (msgno == 0L) 
     || (dest == NULL) || (*dest == NUL_TERM))
    return(SYSCALL_FAILURE);

  data1 = cpystr(mail_fetchheader(mailstream,msgno));
  data2 = cpystr(mail_fetchtext(mailstream,msgno));

  if((data1 == NULL) && (data2 == NULL))
    return(SYSCALL_FAILURE);

  everything = (char *) fs_get(strlen(data1) + strlen(data2) + 1);
  strcpy(everything,data1);
  strcat(everything,data2);

  fs_give((void **) &data1);
  fs_give((void **) &data2);

  INIT(&bs, mail_string,everything,strlen(everything));
  if((bezerk_cache_append(NIL,dest,NIL,NIL, &bs)) == NIL)
    result = SYSCALL_FAILURE;

  fs_give((void **) &everything);
  return(result);
}






syntax highlighted by Code2HTML, v. 0.9.1