/*
**++
**  FACILITY:
**      NEWSVMSMAIL
**
**  ABSTRACT:
**      Routines which use callable mail to map VMS mail into NEWS.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1989
**
**  BUGS:
**      Probably plenty - this is a first pass at the code and the relationship
**      between NEWS and MAIL is not fully worked through!
**
**  MODIFICATION HISTORY:
**      V5.9	14-July-1989	GIH
**        - Initial implementation
**	V6.1	 9-Feb-1992	rankin@eql.caltech.edu
**	  - Lint cleanup; fix some item-list usage problems; create newsmail.h
**	V6.1b6	25-Feb-1993	mark.martinec@ijs.si
**	  - fix swapped params in mail$user_set_info(&context,spl,null_list)
**	    and remove unnecessary call to mail$user_get_info
**	6.1b8	24-Jan-1994	Mark Martinec  mark.martinec@ijs.si
**	  - replace calls to smg$put_chars with calls to smg_put_chars 
**	  - check status returned from all calls to SMG$
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
**	V6.1b9	15-Jan-1995  Charles Bailey  bailey@genetics.upenn.edu
**	  - clean up handling of grp_topreadnum, grp_unread, and grp_funread
**--
**/

#ifdef vaxc
#module NEWSVMSMAIL "V6.2"
#endif

#define _NEWSVMSMAIL_C
#define module_name "NEWSVMSMAIL"

#include "newsinclude.h"
#include "newsextern.h"
#include "newsmail.h"

#define cksts(s) if (!((sts = (s)) & 1)) return(nosysprv(),sts)

#define SCRATCHFILE "SYS$SCRATCH:NEWS_%X_MAIL.TMP"

static
char scratch_file[132],
     wastebasket[40];

static
int sts,
    context = 0,
    mail_grpnum = (1 << 30),
    scratch_file_id = 0;

static
struct mai_itmlst null_list[] = {{0,0,0,0}};

static
struct folders {
  char *folder_name;
  int count;
  GRP_PTR grp;
  struct folders *fnext;
  } *fh = 0, *ft = 0;

static
GRP_PTR mail_newgroup(n,f)
  char *n, *f;
{
  int i,
      j;
  char s[GRPNAMELEN];
  GRP_PTR nentry;

  if ((!n) || (!*n)) return(0);
  util_cvrt(s,n);
  memset((char *)&newsgrp, 0, sizeof(newsgrp));
  strncpy(newsgrp.grp_name,s,GRPNAMELEN_F);
  newsgrp.grp_name[GRPNAMELEN_F-1] = '\0';
  newsgrp.grp_num = mail_grpnum++;
  newsgrp.grp_entdate = time((time_t *) &newsgrp.grp_credate);
  newsgrp.grp_flags = NEWS_M_MAILGROUP | NEWS_M_ACCESS_CHECKED | NEWS_M_WRITE_ACCESS;
  strcpy(newsgrp.grp_topic,"VMS MAIL Folder: ");
  strnncat(newsgrp.grp_topic,sizeof(newsgrp.grp_topic),-1, f,-1, 0);
  ++(ga[0]->grp_count);
  nentry = new_group(&newsgrp);
  nentry->grp_reg = 1;
  for (i = 1; i <= ga_size; ++i)
    if (   !(ga[i]->grp_flags & NEWS_M_MAILGROUP)
        || (strcmp(ga[i]->grp_name,newsgrp.grp_name) > 0)) break;
  if ((i <= ga_size) && !strcmp(ga[i]->grp_name,newsgrp.grp_name))
    nentry = ga[i];
  else {
    if (ga_malloc <= (ga_size + 2)) {
      ga_malloc += 4;
      ga = (GRP_PTR *) news_realloc(ga,ga_malloc * (sizeof nentry));
      }
    if (i <= ga_size) for (j = ga_size; j >= i; --j) ga[j+1] = ga[j];
    if (curr_g >= i) ++curr_g;
    ga[i] = nentry;
    ga[i]->grp_reg = 1;
    ga[i]->grp_display_indx = (cur_dir_type < DIR_NEW);
    ++ga_size;

    if ((gv_size) && (ga[0]->grp_count > gv_size))
      smg$change_virtual_display(&grp_vd,c$rfi(gv_size += 4),&devcol,0,0,0);
    if ((smg_active) && (cur_dir_type < DIR_NEW)) screen_map_dir();
    }
  return(nentry);
}

static int
folder_routine(ud,fn_dsc)
  int *ud;
  struct dsc$descriptor *fn_dsc;
{
  struct folders *tmp;
  char ngname[256];
  int g;

  if (!(fn_dsc->dsc$w_length)) return(1);
  tmp = (struct folders *) news_malloc(sizeof *tmp);
  tmp->fnext = 0;
  tmp->folder_name = (char *) news_malloc(fn_dsc->dsc$w_length + 1);
  strncpy(tmp->folder_name,fn_dsc->dsc$a_pointer,fn_dsc->dsc$w_length);
  tmp->folder_name[fn_dsc->dsc$w_length] = '\0';
  tmp->count = 0;
  if (ft) {
    ft->fnext = tmp;
    ft = tmp;
    }
  else fh = ft = tmp;
  sprintf(ngname,"%s.mail.%s",usr_username,tmp->folder_name);
  util_cvrt(ngname,ngname);
  if ((g = ga_exact_name(ngname)) != 0) tmp->grp = ga[g];
  else tmp->grp = mail_newgroup(ngname,tmp->folder_name);
  return(1);
}

/* mailfile calls */

static
char mailfile_spec[255+1];

int open_mail_file()
{
  unsigned int msl = 0;
  struct mai_itmlst
    mfo_list[]={{sizeof mailfile_spec - 1,MAIL$_MAILFILE_RESULTSPEC,
		 mailfile_spec,&msl}, {0,0,0,0}};

  if (news_captive) return(0);
  if (!mailfile_open) {
    sysprv();
    cksts(mail$mailfile_begin(&context,null_list,null_list));
    cksts(mail$mailfile_open(&context,null_list,mfo_list));
    nosysprv();
    mailfile_open = 1;
    mailfile_spec[msl] = '\0';
    sprintf(scratch_file,SCRATCHFILE,getpid());
    }
  return(1);
}

int close_mail_file()
{
  struct mai_itmlst
    mfc_list[]  = {{0,MAIL$_MAILFILE_FULL_CLOSE,0,0}, {0,0,0,0}};

  if (mailfile_open) {
    cksts(mail$mailfile_close(&context,mfc_list,null_list));
    cksts(mail$mailfile_end(&context,null_list,null_list));
    mailfile_open = 0;
    if (scratch_file_id) {
      delete_file_versions(scratch_file);
      scratch_file_id = 0;
      }
    }
  return(1);
}

static int
dmpurge()
{
  char purge_output[256];
  struct folders *tmp = fh, *tp = 0;
  int dr, ir, md, tr, g = 0;
  struct mai_itmlst
    mfpw_list[]  = {{0,MAIL$_MAILFILE_RECLAIM,0,0}, {0,0,0,0}},
    mfps_list[]  = {{sizeof dr,MAIL$_MAILFILE_DATA_RECLAIM,&dr,0},
		    {sizeof ir,MAIL$_MAILFILE_INDEX_RECLAIM,&ir,0},
		    {sizeof md,MAIL$_MAILFILE_MESSAGES_DELETED,&md,0},
		    {sizeof tr,MAIL$_MAILFILE_TOTAL_RECLAIM,&tr,0},
		    {0,0,0,0}},
    *mpi = null_list,
    *mpo = null_list;

  if (!mailfile_open)
    return(err_line("Error: Purge - Mail file not open"),0);

  dr = ir = md = tr = 0;
  if (cli$present(c$dsc("STATISTICS")) & 1) {
    mpi = mfpw_list;
    mpo = mfps_list;
    }
  else if (cli$present(c$dsc("RECLAIM")) & 1) mpi = mfpw_list;

  mail$mailfile_purge_waste(&context,mpi,mpo);
  if (mpo != null_list)
    sprintf(purge_output,"MAIL Purge: %d messages, %d data bkts, %d indx bkts, %d total",
	  md,dr,ir,tr);
  else strcpy(purge_output,"MAIL Purge - Wastbasket emptied");

	/* now search for and remove the wastebasket folder from news */
  while (tmp) {
    if (!strcmp(tmp->folder_name,wastebasket)) {
      for (g = 1; g <= ga_size; ++g) if (ga[g] == tmp->grp) break;
      if (tp) tp->fnext = tmp->fnext;
      if (ft == tmp) ft = tp;
      if (fh == tmp) fh = tmp->fnext;
      news_free(tmp->folder_name);
      news_free(tmp);
      break;
      }
    tp = tmp;
    tmp = tmp->fnext;
    }
  if (g && (g <= ga_size)) delgrp_mem(g);
  err_line(purge_output);
  return(0);
}

int
do_mail_purge()
{
  return(unwind_display(OUTER_LOOP,dmpurge));
}

int do_mail_compress()
{
  int save_displ;

  if (!mailfile_open)
    return(err_line("Error: COMPRESS - Mail file not open"),0);
  save_displ = leave_screen(0);
  mail$mailfile_compress(&context,null_list,null_list);
  join_screen(save_displ);
  err_line("Mail file compressed");
  return(0);
}

int list_folders()
{
  GRP_PTR gap;
  int msgcontext = 0,
      select_count,
      indx;
  unsigned int wastebasket_len;
  struct mai_itmlst
    mfif_list[] = {{sizeof(int(*)()),MAIL$_MAILFILE_FOLDER_ROUTINE,
		    (void *)folder_routine,0}, {0,0,0,0}},
    mfwb_list[] = {{sizeof wastebasket - 1,MAIL$_MAILFILE_WASTEBASKET,
		    wastebasket,&wastebasket_len}, {0,0,0,0}},
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0,}, {0,0,0,0}},
    mso_list[]  = {{sizeof select_count,MAIL$_MESSAGE_SELECTED,
		    &select_count,0}, {0,0,0,0}};
  struct folders *tmp;

  while (fh) {
    ft = fh->fnext;
    news_free(fh->folder_name);
    news_free(fh);
    fh = ft;
    }
  cksts(mail$mailfile_info_file(&context,mfif_list,mfwb_list));
  wastebasket[wastebasket_len] = '\0';

  if ((tmp = fh) != 0) {
    while (tmp) {
      msgcontext = 0;
      cksts(mail$message_begin(&msgcontext,mb_list,null_list));

      msi_list[0].bl = strlen(tmp->folder_name);
      msi_list[0].ba = tmp->folder_name;
      cksts(mail$message_select(&msgcontext,msi_list,mso_list));
      tmp->count = select_count;
      cksts(mail$message_end(&msgcontext,null_list,null_list));
      gap = tmp->grp;
      gap->grp_count += select_count;
      if (!strcmp(tmp->folder_name,"NEWMAIL")) {
         gap->grp_unread = select_count;
         if ((smg_active) && (indx = gap->grp_display_indx)) {
           if (gap->grp_reg && gap->grp_unread)
             smg$change_rendition(&grp_vd,&indx,c$ac(4),c$ac(1),c$ac(5),c$ac(SMG$M_BOLD),0);
           else
             smg$change_rendition(&grp_vd,&indx,c$ac(4),c$ac(1),c$ac(5),c$ac(0),0);
           sprintf(err_oline,"%5d",gap->grp_unread);
           smg_put_chars(grp_vd,err_oline,indx,73,0,0);
           }
         }
      if ((smg_active) && (gap->grp_display_indx)) {
        sprintf(err_oline,"%5d",gap->grp_count);
	smg_put_chars(grp_vd,err_oline,gap->grp_display_indx,
		      14+displ_subjlen,0,0);
        }
      tmp = tmp->fnext;
      }
    }
  return(1);
}

int mailmap(g)
  int g;
{
  GRP_PTR gap;
  struct folders *tmp;
  int msgcontext = 0,
      select_count;
  unsigned int gdate[2];
  int gid, glines, isnew;
  char gcc[255+1], gfrom[255+1], gsubject[255+1], gto[255+1];
  unsigned int gcc_len, gfrom_len, gsubject_len, gto_len;
  struct mai_itmlst
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0,}, {0,0,0,0}},
    mso_list[]	= {{sizeof select_count,MAIL$_MESSAGE_SELECTED,
		    &select_count,0}, {0,0,0,0}},
    mii_list[]	= {{0,MAIL$_MESSAGE_NEXT,0,0},
		   {0,MAIL$_NOSIGNAL,0,0},
		   {0,0,0,0}},
    mio_list[]	= {{sizeof gdate,MAIL$_MESSAGE_BINARY_DATE,gdate,0},
		   {sizeof gid,MAIL$_MESSAGE_CURRENT_ID,&gid,0},
		   {sizeof glines,MAIL$_MESSAGE_SIZE,&glines,0},
		   {sizeof gcc - 1,MAIL$_MESSAGE_CC,gcc,&gcc_len},
		   {sizeof gfrom - 1,MAIL$_MESSAGE_REPLY_PATH,gfrom,&gfrom_len},
		   {sizeof gsubject - 1,MAIL$_MESSAGE_SUBJECT,gsubject,&gsubject_len},
		   {sizeof gto - 1,MAIL$_MESSAGE_TO,gto,&gto_len},
		   {0,0,0,0}};

  if ((tmp = fh) != 0) {
    while (tmp) {
      if (tmp->grp == ga[g]) {
        gap = ga[g];
        gap->grp_iasize = tmp->count + 1;
        gap->grp_ia = news_malloc((gap->grp_iasize + 1) * sizeof newsitm);
        gap->grp_count = gap->grp_topnum = 0;
        msgcontext = 0;
        cksts(mail$message_begin(&msgcontext,mb_list,null_list));
	msi_list[0].bl = strlen(tmp->folder_name);
	msi_list[0].ba = tmp->folder_name;
	isnew = !strcmp(tmp->folder_name,"NEWMAIL");
        if (mail$message_select(&msgcontext,msi_list,mso_list) & 1) {
          do {
            if ((sts = mail$message_info(&msgcontext,mii_list,mio_list)) & 1) {
              gcc[gcc_len] = '\0';
              gfrom[gfrom_len] = '\0';
              gsubject[gsubject_len] = '\0';
              gto[gto_len] = '\0';

              if (gap->grp_iasize <= gap->grp_count) {
                gap->grp_iasize += 10;
                gap->grp_ia = (ITM_PTR) news_realloc(gap->grp_ia,(gap->grp_iasize + 1) * (sizeof newsitm));
                }
	      newsitm.itm_num = ++gap->grp_topnum;
              newsitm.itm_grp = gap->grp_num;
              sprintf(newsitm.itm_id,"%X",gid);
              newsitm.itm_recvdate = cvt_date((int *) gdate);
              time((time_t *) &newsitm.itm_cachedate);
              newsitm.itm_lines = glines;
              util_subjcpy(newsitm.itm_title,gsubject);
              util_fromcpy(newsitm.itm_from,gfrom);
              newsitm.itm_life = (unsigned short) ~1;
              newsitm.itm_flags = NEWS_M_MAILITEM | NEWS_M_LINESVALID;
              if (isnew) newsitm.itm_flags |= NEWS_M_UNREAD;
              newsitm.itm_cid = gid;
              gap->grp_ia[++gap->grp_count] = newsitm;
              }
            } while (sts & 1);
          }
        cksts(mail$message_end(&msgcontext,null_list,null_list));
        if (isnew) {
          gap->grp_unread = gap->grp_count;
          gap->grp_topreadnum = 0;
          gap->grp_funread = 1;
          }
        else {
          gap->grp_unread = 0;
          gap->grp_topreadnum = gap->grp_topnum;
          gap->grp_funread = gap->grp_topnum + 1;
          }
        if ((gap->grp_display_indx) && (smg_active)) {
          sprintf(err_oline,"%5d",gap->grp_count);
	  smg_put_chars(grp_vd,err_oline,gap->grp_display_indx,
                        14+displ_subjlen,0,0);
          screen_update_gread(g);
          }
        break;
        }
      tmp = tmp->fnext;
      }
    }
  return(1);
}

FILE *read_mail(g,id,file_opened)
  int g, id;
  int *file_opened;     /* returned file descriptor is valid and file is open */
{

  int msgcontext;
  unsigned int buffer_len;
  char buffer[255+2];
  FILE *ftmp;
  GRP_PTR gap;
  struct folders *tmp;
  struct mai_itmlst
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0}, {0,0,0,0}},
    id_list[]	= {{sizeof id,MAIL$_MESSAGE_ID,&id,0},
		   {0,MAIL$_MESSAGE_AUTO_NEWMAIL,0,0},
		   {0,0,0,0}},
    cont_list[] = {{0,MAIL$_MESSAGE_CONTINUE,0,0},
		   {0,MAIL$_MESSAGE_AUTO_NEWMAIL,0,0},
		   {0,MAIL$_NOSIGNAL,0,0},
		   {0,0,0,0}},
    text_list[] = {{sizeof buffer - 2,MAIL$_MESSAGE_RECORD,
		    buffer,&buffer_len}, {0,0,0,0}};

  if (file_opened) *file_opened = 0;
  if ((id == scratch_file_id) && (ftmp = fopen(scratch_file,"r"))) {
    if (file_opened) *file_opened = 1;
    return(ftmp);
    }
  else if (scratch_file_id) {
    delete_file_versions(scratch_file);
    scratch_file_id = 0;
    }

  tmp = fh;
  while (tmp) {
    while (tmp) {
      if (tmp->grp == ga[g]) break;
      tmp = tmp->fnext;
      }
    gap = ga[g];
    msgcontext = 0;
    if (!(mail$message_begin(&msgcontext,mb_list,null_list) & 1)) {
      tmp = tmp->fnext;
      continue;
      }
    msi_list[0].bl = strlen(tmp->folder_name);
    msi_list[0].ba = tmp->folder_name;
    if (!(mail$message_select(&msgcontext,msi_list,null_list) & 1)) {
      tmp = tmp->fnext;
      continue;
      }

    if (!(mail$message_get(&msgcontext,id_list,null_list) & 1)) {
      tmp = tmp->fnext;
      continue;
      }
    if ((ftmp = fopen(scratch_file,"w")) != 0) {
      while (mail$message_get(&msgcontext,cont_list,text_list) & 1) {
        buffer[buffer_len++] = '\n';
        buffer[buffer_len] = '\0';
        fputs(buffer,ftmp);
        }
      fclose(ftmp);
      }
    else tmp = 0;
    mail$message_end(&msgcontext,null_list,null_list);

    if (tmp && !strcmp(tmp->folder_name,"NEWMAIL")) {
      dec_newmail_count();
      remap("MAIL");
      remap("NEWMAIL");
      }

    break;
    }
  if (tmp && (ftmp = fopen(scratch_file,"r"))) {
    if (file_opened) *file_opened = 1;
    scratch_file_id = id;
    return(ftmp);
    }
  else {
    if (file_opened) *file_opened = 0;
    delete_file_versions(scratch_file);
    return(0);
    }
  news_assert_nonfatal(0); return(0);
}

void
remap(foldername)
  const char *foldername;
{
  GRP_PTR gap;
  int sav_context = 0,
      sav_id = 0,
      g;
  struct folders *tmp = fh;

  while (tmp) {
    if (!strcmp(foldername,tmp->folder_name)) break;
    tmp = tmp->fnext;
    }
  if (tmp) {
    if (ga[curr_g] == tmp->grp) {
      sav_context = news_context;
      sav_id = ga[curr_g]->grp_ia[curr_i].itm_cid;
      set_level(1);
      }
    gap = tmp->grp;
    for (g = 1; g <= ga_size; ++g) {

      if (ga[g] == gap) break;
      }
    if (g > ga_size) return;

    if (gap->grp_ia) news_free(gap->grp_ia);
    gap->grp_ia = 0;
    gap->grp_topnum = 0;
    if (gap->grp_iavdsize) smg$delete_virtual_display(&(gap->grp_iavd));
    gap->grp_iavdsize = 0;
    if (sav_context > 1) {
      int j;

      set_level(2);
      for (j = 1; j <= ga[curr_g]->grp_count; ++j)
        if (sav_id == ga[curr_g]->grp_ia[j].itm_cid) break;
      if (j <= ga[curr_g]->grp_count) cur_set_itm(curr_g,j);
      }
    }
  else list_folders();
}

void
update_mail(g)
   int g;
{
  struct folders *tmp = fh;

  while (tmp) {
    if (ga[g] == tmp->grp) break;
    tmp = tmp->fnext;
    }
  if (tmp) remap(tmp->folder_name);
}

int
mail_delfolder(g)
  int g;
{
  struct folders *tmp, *tp = 0, *tt;
  int msgcontext = 0,
      select_count;
  int gid,
      g_deleted = 0;
  struct mai_itmlst
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0,}, {0,0,0,0}},
    mso_list[]	= {{sizeof select_count,MAIL$_MESSAGE_SELECTED,&select_count,0},
		   {0,0,0,0}},
    mii_list[]	= {{0,MAIL$_MESSAGE_NEXT,0,0},
		   {0,MAIL$_NOSIGNAL,0,0},
		   {0,0,0,0}},
    mio_list[]	= {{sizeof gid,MAIL$_MESSAGE_CURRENT_ID,&gid,0},{0,0,0,0}},
    mdi_list[]	= {{sizeof gid,MAIL$_MESSAGE_ID,&gid,0}, {0,0,0,0}};

  if ((tmp = fh) != 0) {
    while (tmp) {
      if ((tmp->grp == ga[g]) && strcmp(tmp->folder_name,wastebasket)) {
        ++g_deleted;
        msgcontext = 0;
        cksts(mail$message_begin(&msgcontext,mb_list,null_list));
	msi_list[0].bl = strlen(tmp->folder_name);
	msi_list[0].ba = tmp->folder_name;
        cksts(mail$message_select(&msgcontext,msi_list,mso_list));
        do {
          if ((sts = mail$message_info(&msgcontext,mii_list,mio_list)) & 1) {
            mail$message_delete(&msgcontext,mdi_list,null_list);
            }
          } while (sts & 1);
        cksts(mail$message_end(&msgcontext,null_list,null_list));
        tt = tmp;
        if (tp) tp->fnext = tmp->fnext;
        if (ft == tmp) ft = tp;
        if (fh == tmp) fh = tmp->fnext;
        tmp = tmp->fnext;
        news_free(tt->folder_name);
        news_free(tt);
        }
      else {
	tp = tmp;
	tmp = tmp->fnext;
	}
      }
    }
  if (g_deleted) {
    delgrp_mem(g);
    remap(wastebasket);
    }
  return 0;
}

int
del_mail_item(g,i)
  int g,i;
{
  struct folders *tmp;
  int i_deleted = 0,
      gid,
      msgcontext = 0;
  struct mai_itmlst
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0}, {0,0,0,0}},
    mdi_list[]	= {{sizeof gid,MAIL$_MESSAGE_ID,&gid,0}, {0,0,0,0}};

  gid = ga[g]->grp_ia[i].itm_cid;
  if ((tmp = fh) != 0) {
    while (tmp) {
      if ((tmp->grp == ga[g]) && strcmp(tmp->folder_name,wastebasket)) {
        ++i_deleted;
        msgcontext = 0;
        cksts(mail$message_begin(&msgcontext,mb_list,null_list));
	msi_list[0].bl = strlen(tmp->folder_name);
	msi_list[0].ba = tmp->folder_name;
        cksts(mail$message_select(&msgcontext,msi_list,null_list));
	mail$message_delete(&msgcontext,mdi_list,null_list);
        cksts(mail$message_end(&msgcontext,null_list,null_list));
	break;
        }
      tmp = tmp->fnext;
      }
    }
  if (i_deleted) {
    delitm_mem(g,i);
    remap(tmp->folder_name);
    remap(wastebasket);
    }
  return 0;
}

void
mail_reset()
{
  mail_grpnum = (1 << 30);
}

int do_open_mail()
{
  set_level(1);
  open_mail_file();
  list_folders();
  do_select("%",1,1);
  return(0);
}

int do_close_mail()
{
  struct folders *tmp = fh, *tt;
  int g;

  set_level(1);
  close_mail_file();
  while ((tt = tmp) != 0) {
    for (g = 1; g <= ga_size; ++g)
      if (ga[g] == tmp->grp) {
        delgrp_mem(g);
        break;
        }
    tmp = tmp->fnext;
    news_free(tt->folder_name);
    news_free(tt);
    }
  return(0);
}

void dec_newmail_count()
{
  int context = 0;
  short new_messages = 0;
  struct mai_itmlst
    upl[] = {{sizeof new_messages,MAIL$_USER_NEW_MESSAGES,
	      &new_messages,0}, {0,0,0,0}},
    spl[] = {{sizeof new_messages,MAIL$_USER_SET_NEW_MESSAGES,
	      &new_messages,0}, {0,0,0,0}};

  sysprv();
  if (   (mail$user_begin(&context,null_list,upl) & 1) && (new_messages > 0)) {
    --new_messages;
    mail$user_set_info(&context,spl,null_list);
    }
  mail$user_end(&context,null_list,null_list);
  nosysprv();
}

int do_move_folder(copy)
  int copy;
{
  int status = 0;
  char folder[80],
       resp[80];
  unsigned short f_l;
  int all,
      sts,
      gid,
      msgcontext;
  struct folders *tmp;
  struct mai_itmlst
    mb_list[]	= {{sizeof context,MAIL$_MESSAGE_FILE_CTX,&context,0},{0,0,0,0}},
    msi_list[]	= {{0,MAIL$_MESSAGE_FOLDER,0,0}, {0,0,0,0}},
    mii_list[]	= {{0,MAIL$_MESSAGE_NEXT,0,0},
		   {0,MAIL$_NOSIGNAL,0,0},
		   {0,0,0,0}},
    mio_list[]	= {{sizeof gid,MAIL$_MESSAGE_CURRENT_ID,&gid,0}, {0,0,0,0}},
    mci_list[]	= {{sizeof gid,MAIL$_MESSAGE_ID,&gid,0},
		   {0,MAIL$_MESSAGE_FOLDER,0,0},
		   {0,MAIL$_NOSIGNAL,0,0},
		   {0,MAIL$_MESSAGE_DELETE,0,0},
		   {0,0,0,0}};
  $DESCRIPTOR(f_d,folder);
  $DESCRIPTOR(r_d,resp);

  if (copy) mci_list[3].ic = 0;
  if (!curr_g || !(ga[curr_g]->grp_flags & NEWS_M_MAILGROUP)) {
    err_line("Not a MAIL folder");
    return(0);
    }
  all = ((news_context == 1) || (cli$present(c$dsc("ALL")) & 1));
  if (cli$get_value(c$dsc("FOLDER"),&f_d,&f_l) == CLI$_ABSENT) {
    char prm[80];

    sprintf(prm,"Folder: %s.mail.",usr_username);
    get_input(&f_d,c$dsc(prm),&f_l);
    }
  if (!f_l || f_l > 39) {
    err_line("Invalid folder name");
    return(0);
    }
  folder[f_l] = '\0';
  s_to_upper(folder);
  mci_list[1].bl = f_l;
  mci_list[1].ba = folder;
  if ((tmp = fh) != 0) {
    while (tmp) {
      if (tmp->grp == ga[curr_g]) {
        msgcontext = 0;
        cksts(mail$message_begin(&msgcontext,mb_list,null_list));
	msi_list[0].bl = f_l;
	msi_list[0].ba = folder;
        if (!(mail$message_select(&msgcontext,msi_list,null_list) & 1)) {
          sprintf(err_oline,"Folder %.200s does not exist.",folder);
          err_line(err_oline);
          get_input(&r_d,c$dsc("Do you want to create it (Y/N)? [y]"),&f_l);
          if (status == RMS$_EOF) *resp = 'n';
          if (!((!f_l) || (tolower(*resp) == 'y'))) {
            cksts(mail$message_end(&msgcontext,null_list,null_list));
            return(0);
            }
          }
	msi_list[0].bl = strlen(tmp->folder_name);
	msi_list[0].ba = tmp->folder_name;
        cksts(mail$message_select(&msgcontext,msi_list,null_list));
        if (!all) {
          gid = ga[curr_g]->grp_ia[curr_i].itm_cid;
          mail$message_copy(&msgcontext,mci_list,null_list);
          }
	else {
          do {
            if ((sts = mail$message_info(&msgcontext,mii_list,mio_list)) & 1) {
              mail$message_copy(&msgcontext,mci_list,null_list);
              }
            } while (sts & 1);
	  }
        cksts(mail$message_end(&msgcontext,null_list,null_list));
        remap(tmp->folder_name);
        remap(folder);
	break;
        }
      tmp = tmp->fnext;
      }
    }
  return(0);
}

static int
dmove()
{
  return(do_move_folder(0));
}

int
do_move()
{
  return(unwind_display(OUTER_LOOP,dmove));
}

static int
dcopy()
{
  return(do_move_folder(1));
}

int
do_copy()
{
  return(unwind_display(OUTER_LOOP,dcopy));
}

static
struct mai_itmlst sm[20];

int get_common_profile()
{
  int context = 0,
      sts;

  sysprv();
  if ((sts = mail$user_begin(&context,null_list,null_list)) & 1) {
    sts = mail$user_get_info(&context,null_list,sm);
    }
  mail$user_end(&context,null_list,null_list);
  nosysprv();
  return(sts & 1);
}

int set_common_profile()
{
  int context = 0,
      sts;

  sysprv();
  if ((sts = mail$user_begin(&context,null_list,null_list)) & 1) {
    sts = mail$user_set_info(&context,sm,null_list);
    }
  mail$user_end(&context,null_list,null_list);
  nosysprv();
  return(sts & 1);
}

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - this is a VMS structure */
#endif
struct mail_sets {
  const char *mtype;
  unsigned short i_code;
  int set_len;
  const char *msg;
  };
#ifdef __DECC
#pragma member_alignment restore
#endif

static const struct mail_sets sms[] =
            {{"auto_purge",MAIL$_USER_SET_AUTO_PURGE,1,
                 "SET MAIL: Automatic deleted message purge is enabled"},
             {"noauto_purge",MAIL$_USER_SET_NO_AUTO_PURGE,3,
                 "SET MAIL: Automatic deleted message purge is disabled"},
             {"cc_prompt",MAIL$_USER_SET_CC_PROMPT,2,
                 "SET MAIL: CC prompt enabled"},
             {"nocc_prompt",MAIL$_USER_SET_NO_CC_PROMPT,3,
                 "SET MAIL: CC prompt disabled"},
             {"copy_self",0,2,
                 "Error: SET MAIL COPY_SELF - parameter required"},
             {"form",MAIL$_USER_SET_FORM,4,
                 "SET MAIL: default print form set to %s"},
             {"noform",MAIL$_USER_SET_NO_FORM,6,
                 "SET MAIL: default print form cleared"},
             {"forward",MAIL$_USER_SET_FORWARDING,4,
                 "SET MAIL: mail will be forwarded to %s"},
             {"noforward",MAIL$_USER_SET_NO_FORWARDING,6,
                 "SET MAIL: forwarding disabled"},
             {"personal_name",MAIL$_USER_SET_PERSONAL_NAME,1,
                 "SET MAIL: personal name set to %s"},
             {"nopersonal_name",MAIL$_USER_SET_NO_PERSONAL_NAME,3,
                 "SET MAIL: personal name field cleared"},
             {"queue",MAIL$_USER_SET_QUEUE,1,
                 "SET MAIL: default print queue set to %s"},
             {"noqueue",MAIL$_USER_SET_NO_QUEUE,3,
                 "SET MAIL: default print queue cleared"},
             {"",0,0,""}};

static const struct mail_sets cms[] =
            {{"send",MAIL$_USER_SET_COPY_SEND,1,
                 "SET MAIL: COPY_SELF SEND set"},
             {"reply",MAIL$_USER_SET_COPY_REPLY,1,
                 "SET MAIL: COPY_SELF REPLY set"},
             {"forward",MAIL$_USER_SET_COPY_FORWARD,1,
                 "SET MAIL: COPY_SELF FORWARD set"},
             {"nosend",MAIL$_USER_SET_NO_COPY_SEND,3,
                 "SET MAIL: COPY_SELF SEND disabled"},
             {"noreply",MAIL$_USER_SET_NO_COPY_REPLY,1,
                 "SET MAIL: COPY_SELF REPLY disabled"},
             {"noforward",MAIL$_USER_SET_NO_COPY_FORWARD,1,
                 "SET MAIL: COPY_SELF FORWARD disabled"},
             {"",0,0,""}};

int do_set_mail()
{
  char mt[80];
  unsigned short len;
  $DESCRIPTOR(dsc,mt);
  int i = 0;


  if (cli$get_value(c$dsc("MAILTYPE"),&dsc,&len) & 1) {
    mt[len] = '\0';
    lower_case(mt);
    while (sms[i].set_len) {
      if (!strncmp(mt,sms[i].mtype,max(len,sms[i].set_len))) {
        if (i == 4) {
          if (cli$get_value(c$dsc("QUAL"),&dsc,&len) & 1) {
            mt[len] = '\0';
            lower_case(mt);
            i = 0;
            while (cms[i].set_len) {
              if (!strncmp(mt,cms[i].mtype,max(len,cms[i].set_len))) {
		sm[0] = sm[1] = null_list[0];
                sm[0].ic = cms[i].i_code;
                if (set_common_profile()) err_line(cms[i].msg);
                return(0);
                }
              ++i;
              }
            }
          err_line(sms[4].msg);
          return(0);
          }
        if ((i == 5) || (i == 7) || (i == 9) || (i == 11)) {
          if (cli$get_value(c$dsc("QUAL"),&dsc,&len) & 1) {
            mt[len] = '\0';
            sm[0].ic = sms[i].i_code;
            sm[0].bl = len;
	    sm[0].ba = mt;
            sm[0].rl = 0;
	    sm[1] = null_list[0];
            if (set_common_profile()) {
              sprintf(err_oline,sms[i].msg,mt);
              err_line(err_oline);
              }
            }
          return(0);
          }
	sm[0] = sm[1] = null_list[0];
        sm[0].ic = sms[i].i_code;
        if (set_common_profile())
          err_line(sms[i].msg);
        return(0);
        }
      ++i;
      }
    err_line("Error: SET MAIL - unrecognised mail parameter");
    }
  else
    err_line("Error: SET MAIL - mail parameter required");
  return(0);
}

static int dsmail()
{
  char mt[255+1], fw_val[127+1], pn_val[127+1], fm_val[39+1], qu_val[39+1];
  int i = 0,
      ap_sw = 0, ap_val = 0,
      cc_sw = 0, cc_val = 0,
      cs_sw = 0, cf_val = 0, cr_val = 0, cs_val = 0,
      fm_sw = 0,
      fw_sw = 0,
      nm_sw = 0,
      pn_sw = 0,
      qu_sw = 0,
      all = 0;
  unsigned short len,
	nm_val;
  unsigned int   fm_len,
        fw_len,
        pn_len,
        qu_len;
  $DESCRIPTOR(dsc,mt);

  if (cli$get_value(c$dsc("MAILTYPE"),&dsc,&len) & 1) {
    mt[len] = '\0';
    lower_case(mt);
    if (!strncmp(mt,"all",len)) all = 1;
    }
  else all = 1;
  
  if (all || !strncmp(mt,"auto_purge",len)) {
    ap_sw = 1;
    sm[i].bl = sizeof ap_val;
    sm[i].ic = MAIL$_USER_AUTO_PURGE;
    sm[i].ba = &ap_val;
    sm[i].rl = 0;
    ++i;
    }
  if (all || !strncmp(mt,"cc_prompt",len)) {
    cc_sw = 1;
    sm[i].bl = sizeof cc_val;
    sm[i].ic = MAIL$_USER_CC_PROMPT;
    sm[i].ba = &cc_val;
    sm[i].rl = 0;
    ++i;
    }
  if (all || !strncmp(mt,"copy_self",len)) {
    cs_sw = 1;
    sm[i].bl = sm[i+1].bl = sm[i+2].bl = sizeof(int);
    sm[i].ic = MAIL$_USER_COPY_FORWARD;
    sm[i+1].ic = MAIL$_USER_COPY_REPLY;
    sm[i+2].ic = MAIL$_USER_COPY_SEND;
    sm[i].ba = &cf_val;
    sm[i+1].ba = &cr_val;
    sm[i+2].ba = &cs_val;
    sm[i].rl = sm[i+1].rl = sm[i+2].rl = 0;
    i += 3;
    }
  if (all || !strncmp(mt,"form",len)) {
    fm_sw = 1;
    sm[i].bl = sizeof fm_val - 1;
    sm[i].ic = MAIL$_USER_FORM;
    sm[i].ba = fm_val;
    sm[i].rl = &fm_len;
    fm_len = 0;
    ++i;
    }
  if (all || !strncmp(mt,"forward",len)) {
    fw_sw = 1;
    sm[i].bl = sizeof fw_val - 1;
    sm[i].ic = MAIL$_USER_FORWARDING;
    sm[i].ba = fw_val;
    sm[i].rl = &fw_len;
    fw_len = 0;
    ++i;
    }
  if (all || !strncmp(mt,"new_mail_count",len)) {
    nm_sw = 1;
    sm[i].bl = sizeof nm_val;
    sm[i].ic = MAIL$_USER_NEW_MESSAGES;
    sm[i].ba = (char *) &nm_val;
    sm[i].rl = 0;
    ++i;
    }
  if (all || !strncmp(mt,"personal_name",len)) {
    pn_sw = 1;
    sm[i].bl = sizeof pn_val - 1;
    sm[i].ic = MAIL$_USER_PERSONAL_NAME;
    sm[i].ba = pn_val;
    sm[i].rl = &pn_len;
    pn_len = 0;
    ++i;
    }
  if (all || !strncmp(mt,"queue",len)) {
    qu_sw = 1;
    sm[i].bl = sizeof qu_val - 1;
    sm[i].ic = MAIL$_USER_QUEUE;
    sm[i].ba = qu_val;
    sm[i].rl = &qu_len;
    qu_len = 0;
    ++i;
    }
  sm[i] = null_list[0];
  if (get_common_profile()) {
    if (all) {
      start_header(T_DISPLAY_LOOP,SHOW_MAILPROFILE);
      put_line("SHOW MAIL: Mail profile settings",0,T_DISPLAY_LOOP);
      put_line("",0,T_DISPLAY_LOOP);
      end_header(T_DISPLAY_LOOP);
      }
    if (nm_sw) {
      sprintf(mt,"  %d new mail messages",nm_val);
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (pn_sw) {
      pn_val[pn_len] = '\0';
      if (!pn_len) strcpy(mt,"  No personal name defined");
      else sprintf(mt,"  Personal name is \"%s\"",pn_val);
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (fw_sw) {
      fw_val[fw_len] = '\0';
      if (!fw_len) strcpy(mt,"  No forwarding address set");
      else sprintf(mt,"  Forwarding address is \"%s\"",fw_val);
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (cc_sw) {
      sprintf(mt,"  CC prompt is %s", (cc_val ? "enabled" : "disabled"));
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (cs_sw) {
      if (cf_val || cr_val || cs_val) 
	sprintf(mt,"  Copy_self is enabled for %s %s %s",
               (cf_val ? "FORWARD" : ""),
               (cr_val ? "REPLY" : ""),
               (cs_val ? "SEND" : ""));
      else sprintf(mt,"  Copy_self is disabled");
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (ap_sw) {
      sprintf(mt,"  Automatic deleted message purge is %s",
                   (ap_val ? "enabled" : "disabled"));
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (qu_sw) {
      qu_val[qu_len] = '\0';
      if (!qu_len) strcpy(qu_val,"SYS$PRINT");
      sprintf(mt,"  Default print queue is %s",qu_val);
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (fm_sw) {
      fm_val[fm_len] = '\0';
      if (!fm_len) strcpy(mt,"  No default print form set");
      else sprintf(mt,"  Default print form is %s",fm_val);
      if (all) put_line(mt,0,T_DISPLAY_LOOP);
      else {
        strcpy(err_oline,"SHOW MAIL:"); err_oline_cat(mt);
        err_line(err_oline);
        }
      }
    if (all) put_line("",1,T_DISPLAY_LOOP);
    }
  return(0);
}

int
do_show_mail()
{
  return(unwind_display(I_DISPLAY_LOOP,dsmail));
}
