/*
**++
**  FACILITY:
**      NEWSDELETE.C
**
**  ABSTRACT:
**      This module deletes newsitems and newsgroups from the local database.
**      The module also contains the implementation of a history file of item
**      identifiers, used in prevention of NEWS loops.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990
**
**  Modification History:
**   6.1b7  15-May-1993  Charles Bailey  bailey@genetics.upenn.edu
**    - optimization of item number lookup
**    - delete proper item range when SUBJECTORDER set
**   6.1b8  13-Sep-1993  Charles Bailey  bailey@genetics.upenn.edu
**    - zero empty grp_ia cell at end of array after deleting item so
**      find_itm_by_num doesn't think it's a valid item
**   6.1b8  22-Dec-1993  Mark Martinec  mark.martinec@ijs.si
**    - convert calls to RMS to use new sys_* macros for error handling 
**   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$
**   6.1b8  15-May-1994  Mark Martinec  mark.martinec@ijs.si
**    - add 'if (smg_active)' before all calls to newsgroup_header()
**      to avoid SMG calls which would fail (problem manifested
**      itself as SMG failures during DELETE ITEM in 'noscreen' mode)
**   6.1b9  25-May-1994  Charles Bailey  bailey@genetics.upenn.edu
**    - add dummy flk param to do_oitem calls
**   6.1b9  14-Jun-1994 Saul Tannenbaum saul@hnrc.tufts.edu
**    - add rms tuning hooks
**   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.1b11 21-Feb-1997  Mark Martinec   mark.martinec@ijs.si
**    - fp_open is now int;  pass it by ref;
**      don't try to delete_file_versions(fpd_open);
**   V6.1b11 14-Apr-1997  Mark Martinec   mark.martinec@ijs.si
**    - change %s to %.<n>s in sprintf to guard against overwriting buffer
**    - add attribute 'const to hist_check()'s parameter
**    - replace several calls to strcpy/strcat with safer strnncpy/strnncat
**    - fix the year 2000 problem when generating the
**      'Date:' header line for CANCEL posting
**--
**/

#ifdef vaxc
#module NEWSDELETE "V6.2"
#endif

#define _NEWSDELETE_C
#define module_name "NEWSDELETE"

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

extern char dir_sincestr[];

static int no_confirm = 0;

/*
 *  HISTORY routines
 *
 *  All the necessary history file operations have been placed in this module
 */
#if NNTP_CLIENT_ONLY

int set_nohistory() { return(0); }

int set_history() { return(0); }

int do_show_history() { return(0); }

int open_hist_file() { return(0); }

void close_hist_file() { return; }

int hist_check(id) const char *id; { return(0); }

int hist_add(id) char *id; { return(1); }

int hist_skim(deldate) unsigned int deldate; { return(0); }

#else

static int hist_file_open = 0,
           hist_off = 0;

static struct FAB histfab;

static struct RAB histrab;

static struct XABKEY histkey_1,
                     histkey_2;

static struct XABPRO histpro_1;

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - file record */
#endif
static struct hist {
                    char hist_id[IDLEN];
                    unsigned int hist_date;
                   } newshist;
#ifdef __DECC
#pragma member_alignment restore
#endif

int
set_nohistory()
{
  FILE *fpw;
  time_t cur_time;

  if (nntp_client) return(0);
  if (no_priv()) {
    err_line("Error: Set History - No privilege");
    return(0);
    }
  close_hist_file();
  time(&cur_time);
  sysprv();
  if (!(fpw = fopen(HIST_OFF,"r"))) {
    if ((fpw = fopen(HIST_OFF,"w")) != 0) {
      fprintf(fpw,"NEWS - no History - %s: %s)",usr_username,ctime(&cur_time));
      fclose(fpw);
      hist_off = 1;
      }
    }
  else {
    fclose(fpw);
    hist_off = 1;
    }
  nosysprv();
  if (hist_off) err_line("History function disabled");
  else err_line("Error: Cannot disable history function");
  return(0);
}

int
set_history()
{
  char sw[20];
    unsigned short sw_len;
  $DESCRIPTOR(sw_dsc,sw);

  if (nntp_client) return(0);
  if (cli$present(c$dsc("BOOL")) & 1) {
    if (cli$get_value(c$dsc("BOOL"),&sw_dsc,&sw_len) & 1) {
      sw[sw_len] = '\0';
      if (!strncmp(sw,"OF",2)) return(set_nohistory(),0);
      }
    }
  if (no_priv()) return(err_line("Error: Set History - No privilege"),0);
  sysprv();
  delete_file_versions(HIST_OFF);
  nosysprv();
  err_line("History function enabled");
  hist_off = 0;
  return(0);
}

int
do_show_history()
{
  if (nntp_client) return(0);
  if (hist_off || (!open_hist_file())) err_line("History function disabled");
  else err_line("History function enabled");
  return(0);
}

int
open_hist_file()
{
  int status;
  FILE *fpw;
/* ST 3/2/93 - RMS Performance Options for History file access */
  int buff_count=0;
  char *buff_count_ptr;
  int hist_performance = FALSE;


  if (nntp_client) return(0);
  if (hist_off) return(0);
  sysprv();
  if ((fpw = fopen(HIST_OFF,"r")) != 0) {
    fclose(fpw);
    if (hist_file_open) sys_close(&histfab);
    nosysprv();
    hist_file_open = 0;
    hist_off = 1;
    return(0);
    }
  if (hist_file_open) return(nosysprv(),1);

  histfab = cc$rms_fab;
  histfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_DEL;
  histfab.fab$l_fna = (char *) HIST_FILE;
  histfab.fab$b_fns = strlen(histfab.fab$l_fna);
  histfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

/* ST 3/2/93 RMS Performance Patches */
  buff_count_ptr = news_getenv("NEWS_HISTORYFILE_BUFFERCOUNT",0);
  if (buff_count_ptr != NULL)
	{
	 sscanf(buff_count_ptr,"%d",&buff_count);
	 hist_performance = TRUE;
	}

  if (news_getenv("NEWS_HISTORYFILE_DFW",0) != 0)
		 histfab.fab$l_fop |= FAB$M_DFW; /* deferred write */
  if (news_getenv("NEWS_HISTORYFILE_RTV",0) != 0)
		 histfab.fab$b_rtv = 255;	/* map the entire file */
  if (!sys_open_nofnf(&histfab)) {
    histfab.fab$b_bks = 4;
    histfab.fab$l_fop = FAB$M_CIF;
    histfab.fab$w_mrs = sizeof newshist;
    histfab.fab$b_org = FAB$C_IDX;
    histfab.fab$b_rat = FAB$M_CR;
    histfab.fab$b_rfm = FAB$C_FIX;
    histfab.fab$l_xab = (char *) &histkey_1;

    histkey_1 = cc$rms_xabkey;
    histkey_1.xab$b_dtp = XAB$C_STG;
    histkey_1.xab$b_flg = 0;
    histkey_1.xab$w_pos0 = (char *) &newshist.hist_id - (char *) &newshist;
    histkey_1.xab$b_ref = 0;
    histkey_1.xab$b_siz0 = IDLEN;
    histkey_1.xab$l_nxt = (char *) &histkey_2;

    histkey_2 = cc$rms_xabkey;
    histkey_2.xab$b_dtp = XAB$C_BN4;
    histkey_2.xab$b_flg = XAB$M_DUP;
    histkey_2.xab$w_pos0 = (char *) &newshist.hist_date - (char *) &newshist;
    histkey_2.xab$b_ref = 1;
    histkey_2.xab$b_siz0 = 4;
    histkey_2.xab$l_nxt = (char *) &histpro_1;

    histpro_1 = cc$rms_xabpro;
    histpro_1.xab$w_pro = 0xEE00;
    if (!sys_create(&histfab)) return(nosysprv(),0);
    }

  histrab = cc$rms_rab;
  histrab.rab$b_krf = 0;
  histrab.rab$l_fab = &histfab;
  histrab.rab$l_ubf = (char *) &newshist;
  histrab.rab$w_usz = sizeof newshist;
  histrab.rab$l_rbf = (char *) &newshist;
  histrab.rab$w_rsz = sizeof newshist;

/* ST 3/2/93 RMS Performance Patches */
    if (hist_performance) 
	histrab.rab$b_mbf = (buff_count <= 127) ? buff_count : 127;

    if (news_getenv("NEWS_HISTORYFILE_FASTDELETE",0) != 0)
	histrab.rab$l_rop |= RAB$M_FDL;
	
  if (!sys_connect(&histrab)) {
    sys_close(&histfab);
    nosysprv();
    return(0);
    }
  nosysprv();
  return(hist_file_open = 1);
}

void
close_hist_file()
{
  int status;

  if (nntp_client) return;
  if (hist_file_open) {
    sysprv();
    sys_close(&histfab);
    nosysprv();
    }
  hist_file_open = 0;
}

int
hist_check(id)
    const char *id;
{
  int status;
  char l_id[IDLEN];

  if (nntp_client) return(0);
  if (hist_off || (!open_hist_file())) return(0);
  util_idcpy(l_id,id);
  histrab.rab$l_kbf = l_id;
  histrab.rab$b_krf = 0;
  histrab.rab$b_ksz = IDLEN;
  histrab.rab$l_rop= RAB$M_RRL | RAB$M_NLK ;
  histrab.rab$b_rac = RAB$C_KEY;
  return(sys_find_nornf(&histrab));
}

int
hist_add(id)
    char *id;
{
  int status;

  if (nntp_client) return(0);
  if (hist_off) return(1);
  if (!open_hist_file()) return(0);
  time((time_t *) &newshist.hist_date);
  util_idcpy(newshist.hist_id,id);
  histrab.rab$b_rac = RAB$C_KEY;
  histrab.rab$l_rop = RAB$M_WAT;
  return(sys_put_nodup(&histrab));
}

int
hist_skim(deldate)
    unsigned int deldate;
{
  int status;
  int mod, records = 0;

  if (nntp_client) return(records);
  if (hist_off) return(records);
  if (!open_hist_file()) return(0);
  histrab.rab$b_rac = RAB$C_SEQ;
  histrab.rab$b_krf = 1;
  histrab.rab$l_rop = RAB$M_WAT;
  sys_rewind(&histrab);
  mod = 1000;
  while (sys_get_noeof(&histrab)) {
    if (newshist.hist_date >= deldate) break;
    if (records%mod == 0) {
      char now[9];
      time_t t = time(0);
      strncpy(now,ctime(&t)+11,8);
      printf("\tDeleting record %d from %20.20s (%8.8s)\n",records+1,
             ctime((time_t *) &(newshist.hist_date))+4, now);
      if (records >= 10000) mod = 10000;
      }
    if (sys_delete(&histrab)) ++records;
    }
  return(records);
}
#endif

/*
 *  parse_items
 *
 *  Parse a list of item numbers of the form: num, num-num, or '*', placing
 *  the result in the structure d_itm
 */

void parse_items(s,d_itm,cg,ci)
  char *s;
  NOTE_RANGE *d_itm;
  int cg, ci;
{
  char *cp1, *cp2, *cp3;
  int locnum1, locnum2,
      l,
      di = 0;

  d_itm[0].fitm = 0;
  if (!cg) {
    err_line("No Group Selected");
    return;
    }

  map_items(cg);
  if ((!*s) || (!ga[cg]->grp_count)) return;
  lower_case(s);
  for (cp1=s; cp1; cp1=cp3) {
    d_itm[di].fitm = 0;
    d_itm[di].litm = 0;
    d_itm[di].ngrp = cg;

    cp3 = chop_str_plus(cp1,',');

    if ((cp2 = strchr(cp1,'-')) != 0) {
      *cp2++ = '\0';
      if (!sscanf(cp1,"%d",&locnum1)) {
        if (!(l = strlen(cp1))) locnum1 = ga[cg]->grp_ia[ci].itm_num;
        else if (!strncmp(cp1,"first",min(l,5))) locnum1 = ga[cg]->grp_ia[1].itm_num;
        else if (!strncmp(cp1,"last",min(l,4))) locnum1 = ga[cg]->grp_ia[(ga[cg]->grp_count)].itm_num;
        else if (!strcmp(cp1,".")) locnum1 = ga[cg]->grp_ia[ci].itm_num;
        else if (!strcmp(cp1,"*")) locnum1 = ga[cg]->grp_ia[(ga[cg]->grp_count)].itm_num;
        else continue;
        }
      if (!sscanf(cp2,"%d",&locnum2)) {
        if (!(l = strlen(cp2))) locnum2 = ga[cg]->grp_ia[ci].itm_num;
        else if (!strncmp(cp2,"first",min(l,5))) locnum2 = ga[cg]->grp_ia[1].itm_num;
        else if (!strncmp(cp2,"last",min(l,4))) locnum2 = ga[cg]->grp_ia[(ga[cg]->grp_count)].itm_num;
        else if (!strcmp(cp2,".")) locnum2 = ga[cg]->grp_ia[ci].itm_num;
        else if (!strcmp(cp2,"*")) locnum2 = ga[cg]->grp_ia[(ga[cg]->grp_count)].itm_num;
        else continue;
        }

      if (locnum1 > locnum2) {
        int t = locnum1;
        locnum1 = locnum2;
        locnum2 = t;
        }
      if (!locnum1) locnum1 = 1;
      d_itm[di].fitm = locnum1;
      d_itm[di].litm = locnum2;
      }
    else if (*cp1 == '*') d_itm[di].fitm = -1;
    else if ((sscanf(cp1,"%d",&locnum1) == 1) && locnum1) d_itm[di].fitm = locnum1;
    else if (!(l = strlen(cp1)));
    else if (!strncmp(cp1,"first",min(l,5))) d_itm[di].fitm = ga[cg]->grp_ia[1].itm_num;
    else if (!strncmp(cp1,"last",min(l,4))) d_itm[di].fitm = ga[cg]->grp_ia[(ga[cg]->grp_count)].itm_num;
    else if (!strcmp(cp1,".")) d_itm[di].fitm = ga[cg]->grp_ia[ci].itm_num;
    else if (!strncmp(cp1,"all",min(l,3))) d_itm[di].fitm = -1;
    if (d_itm[di].fitm) {
      if (d_itm[di].fitm == -1) ++di;
      else if (!d_itm[di].litm) {
        int j;
        for (j = 1; j <= ga[cg]->grp_count; ++j)
          if (d_itm[di].fitm <= ga[cg]->grp_ia[j].itm_num) break;
        if (d_itm[di].fitm == ga[cg]->grp_ia[j].itm_num) ++di;
	}
      else {
        int rf = 0, rl = 0, j;
        for (j = 1; j <= ga[cg]->grp_count; ++j) {
          if (!rf && (d_itm[di].fitm <= ga[cg]->grp_ia[j].itm_num))
	    rf = ga[cg]->grp_ia[j].itm_num;
          if (d_itm[di].litm >= ga[cg]->grp_ia[j].itm_num)
            rl = ga[cg]->grp_ia[j].itm_num;
	  else break;
          }
        if (rf && (rf <= rl)) {
	  d_itm[di].fitm = rf;
          d_itm[di].litm = rl;
          ++di;
          }
        }
      }
    d_itm[di].fitm = 0;
    }
}

/*
 *  news_delete_file
 *
 *  Delete the file associated with a news item
 */

void
news_delete_file(itm,grp_name)
  int itm;
  char *grp_name;
{
#if NNTP_CLIENT_ONLY
  return;
#else
  char fn[256];

  if (nntp_client) return;
  sprintf(fn,Itm_template,util_dir(grp_name),itm);
  sysprv();
  delete_file_versions(fn);
  nosysprv();
#endif
}

/*
 *  delgrp_mem
 *
 *  delete a newsgroup from mem arrays and the screen
 */

void
delgrp_mem(g)
  int g;
{
  int status;
  int j, dsp, sg, rend;
  GRP_PTR gap;
  char dir_type[120];

  if (ga[0]->grp_count > 0) --(ga[0]->grp_count);
  set_level(1);
  if ((curr_g == g) && (cur_down_grp(1,0) != 1)) cur_up_grp(1,0);

  gap = ga[g];
  if (gap->grp_ia) news_free(gap->grp_ia);
  if (gap->grp_idtail) {
    while ((gap->grp_idtail = (gap->grp_idtail)->b_link))
      news_free((gap->grp_idtail)->f_link);
    news_free(gap->grp_idhead);
    }
  if (gap->grp_iavdsize) _c$cks(smg$delete_virtual_display(&(gap->grp_iavd)));
  if ((dsp = gap->grp_display_indx) && (curr_g > g)) --curr_g;
  for (j = g + 1; j <= ga_size; ++j) {
    ga[j-1] = ga[j];
    if ((dsp) && (ga[j]->grp_display_indx)) {
      if (smg_active) {
        sprintf(err_oline,"%-5d",ga[j]->grp_display_indx - 1);
        rend = ((ga[j]->grp_reg) && (ga[j]->grp_unread)) ? SMG$M_BOLD : 0;
        smg_put_chars(grp_vd,err_oline,ga[j]->grp_display_indx,4,0,rend);
        }
      --ga[j]->grp_display_indx;
      }
    }
  if (dsp) {
    if (smg_active) {
      if (g == grp_display_size)
        _c$cks(smg$erase_display(&grp_vd,&grp_display_size,c$ac(1)));
      else
        _c$cks(smg$scroll_display_area(&grp_vd,&dsp,c$ac(1),0,0,
               c$ac(SMG$M_UP),c$ac(1)));
      }
    if (!--grp_display_size) {
      curr_g = 0;
      g_arrow = 0;
      }
    }
  --ga_size;
  news_free(gap);
  if (smg_active) {
    if (curr_g) cur_set_grp(curr_g);
    sg = grp_display_size;
    if (cur_dir_type == DIR_DATE)
      sprintf(dir_type,"NEWSGROUPS %.60s [SINCE=%s, %d Newsgroups]",class_name(),dir_sincestr,sg);
    else if (cur_dir_type == DIR_NEW)
      sprintf(dir_type,"NEWSGROUPS %.60s [NEW, %d Newsgroups]",class_name(),sg);
    else if (cur_dir_type == DIR_REGISTER)
      sprintf(dir_type,"NEWSGROUPS %.60s [REGISTERED, %d Newsgroups]",class_name(),sg);
    else
      sprintf(dir_type,"NEWSGROUPS %.60s [ALL, %d Newsgroups]",class_name(),sg);
    smg_put_chars(grp_header_vd,dir_type,1,4,1,0);
    }
}

/*
 *  delgrp_file
 *
 *  delete a newsgroup from index files
 */

void
delgrp_file(g)
  int g;
{
  if (ga[g]->grp_flags & NEWS_M_MAILGROUP) {
    mail_delfolder(g);
    return;
    }

#if !NNTP_CLIENT_ONLY
  if (!nntp_client) {
    int status;
    unsigned int key[2];
    int grp_in_file = 0;

    grprab.rab$l_kbf = (char *) &(ga[g]->grp_num);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;
    grp_in_file = sys_get_nornf(&grprab);

    key[0] = 0;
    key[1] = ga[g]->grp_num;

    itmrab.rab$l_kbf = (char *) &key;
    itmrab.rab$l_rop = RAB$M_WAT | RAB$M_KGE;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$b_ksz = 8;
    itmrab.rab$b_krf = 0;
    while (sys_get_nornf(&itmrab)) {
      if (newsitm.itm_grp != ga[g]->grp_num) {
        sys_free(&itmrab);
        break;
        }
      itmrab.rab$l_rop = RAB$M_WAT;
      itmrab.rab$b_rac = RAB$C_SEQ;
      sys_delete(&itmrab);
      hist_add(newsitm.itm_id);
      news_delete_file(newsitm.itm_num,ga[g]->grp_name);
      }
    if (grp_in_file) {
      sys_delete(&grprab);

      grprab.rab$l_kbf = (char *) c$rfi(0);
      grprab.rab$b_ksz = 4;
      grprab.rab$b_krf = 1;
      grprab.rab$l_rop = RAB$M_WAT;
      grprab.rab$b_rac = RAB$C_KEY;
      sys_get(&grprab);
      if (newsgrp.grp_count > 0) --newsgrp.grp_count;
      sys_update(&grprab);
      }
    }
#endif
/*  do_set_norestrict(g);   - this call makes no sense in this context ! */
  delgrp_mem(g);
}

/*
 *  del_grp
 *
 *  Delete the group indexed by g
 */

int
del_grp(g)
  int g;
{
  int status;

  if (no_confirm) return(delgrp_file(g),1);
  if (!smg_active) {
    printf("\nGroup to delete:\n");
    printf("\tName   : %s\n",ga[g]->grp_name);
    printf("\tCount  : %d\n",ga[g]->grp_count);
    printf("\tCreated: %s",ctime((time_t *) &(ga[g]->grp_credate)));
    printf("\tUpdated: %s\n",ctime((time_t *) &(ga[g]->grp_entdate)));
    }

  if (ga[g]->grp_count) {
    sprintf(err_oline,"WARNING - Group contains %d items\007\007",ga[g]->grp_count);
    err_line(err_oline);
    }
  else clear_err_line();

  sprintf(err_oline,"Delete Group: %.200s? [n]:",ga[g]->grp_name);
  status = get_input(&usr_inp_dsc,c$dscl(&err_oline_d),&usr_inp_l);
  clear_err_line();
  if (status == RMS$_EOF) return(-1);
  if ((status & 1) && (usr_inp_l) && (tolower(*usr_inp) == 'y'))
    return(delgrp_file(g),1);
  else return(0);
  return(0);
}

/*
 *  delitm_mem
 *
 *  Delete a single news item from mem and screen
 */

int
delitm_mem(g,i)
  int g,
      i;
{
  int status;
  ITM_PTR iap;
  GRP_PTR gap;
  int j, s_mapped, uread;

  gap = ga[g];
  if (!gap->grp_ia) map_items(g);
  iap = gap->grp_ia;

  if (!find_itm_by_num(g,i,&i)) return(0);

  uread = (iap[i].itm_flags & NEWS_M_UNREAD);
  if ((gap->grp_c_itm == i) && (cur_down_itm(g,1,0) != 1)) cur_up_itm(g,1,0);
  s_mapped = gap->grp_iavdsize;
  for (j = i+1; j <= gap->grp_count; ++j) iap[j-1] = iap[j];

  /* clear empty cell at end of ia so it doesn't look like a real item */
  /* 13-Sep-1993  Charles Bailey  bailey@genetics.upenn.edu */
  memset(&(iap[gap->grp_count]),0,sizeof newsitm);

  if (gap->grp_c_itm > i) {
    --gap->grp_c_itm;
    if ((news_context > 1) && (curr_g == g)) curr_i = gap->grp_c_itm;
    }
  if (s_mapped) {
    if (i == gap->grp_count)
      _c$cks(smg$erase_display(&(gap->grp_iavd),&i,c$ac(1)));
    else {
      _c$cks(smg$scroll_display_area(&(gap->grp_iavd),&i,c$ac(1),0,0,c$ac(SMG$M_UP),c$ac(1)));
      position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0,gap->grp_count);
      }
    }
  if ((!gap->grp_count) || (!--gap->grp_count)) {
    if (s_mapped) {
      _c$cks(smg$erase_display(&(gap->grp_iavd)));
      smg_put_chars(gap->grp_iavd,"NEWSGROUP is empty",1,20,0,SMG$M_REVERSE);
      if (gap->grp_iapaste != 4) {
        if ((news_context > 1) && (smg_active) && (curr_g == g))
          _c$cks(smg$move_virtual_display(&(gap->grp_iavd),&pid,c$ac(4),c$ac(1),0));
        gap->grp_iapaste = 4;
        }
      }
    gap->grp_c_itm = 0;
    if ((news_context > 1) && (curr_g == g)) curr_i = 0;
    }

  if ((uread) && (gap->grp_unread)) --gap->grp_unread;
  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);
    }
  if (uread) screen_update_gread(g);
  return 1;
}

/*
 *  delitm_file
 *
 *  Delete a single news item from files
 */

static void delitm_file(g,i)
  unsigned int g, i;
{
  if (ga[g]->grp_flags & NEWS_M_MAILGROUP) del_mail_item(g,i);

#if !NNTP_CLIENT_ONLY
  if (!nntp_client) {
    int status;
    unsigned int ikey[2];
    int grp_in_file = 0;

    ikey[0] = i;
    ikey[1] = ga[g]->grp_num;

    itmrab.rab$l_kbf = (char *) ikey;
    itmrab.rab$l_rop = RAB$M_WAT;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$b_ksz = 8;
    itmrab.rab$b_krf = 0;

    grprab.rab$l_kbf = (char *) itmrab.rab$l_kbf + 4;
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    grp_in_file = sys_get_nornf(&grprab);
    if (sys_get_nornf(&itmrab)) {
      sys_delete(&itmrab);
      hist_add(newsitm.itm_id);
      news_delete_file(newsitm.itm_num,ga[g]->grp_name);
      }

    if (grp_in_file) {
      ikey[0] = 0;
      itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
      itmrab.rab$b_rac = RAB$C_KEY;
      if (!sys_get_nornf(&itmrab) || (newsitm.itm_grp != newsgrp.grp_num))
        newsgrp.grp_firstnum = newsgrp.grp_topnum + 1;
      else newsgrp.grp_firstnum = newsitm.itm_num;
      sys_free_nornl(&itmrab);
      if (newsgrp.grp_count > 0) --newsgrp.grp_count;
      sys_update(&grprab);
      }
    }
#endif
  delitm_mem(g,i);
}

/*
 *  del_id
 *
 *  Delete an item, given the identifier value
 */

#if NNTP_CLIENT_ONLY

int del_id(id,sender) char *id, *sender; { return(0); }

#else

int del_id(id,sender)
  char *id, *sender;
{
  int status;
  unsigned int ikey[2];
  char mref[IDLEN + 4];
  int g, *gp, i, ret_val = 0, del_loop = 0, si, sg;

  if (nntp_client) return(0);

  util_idcpy(mref,id);
  gp = (int *) &mref[IDLEN];
  *gp = 0;

  itmrab.rab$l_kbf = mref;
  itmrab.rab$b_ksz = IDLEN + 4;
  itmrab.rab$b_krf = 1;
  itmrab.rab$l_rop = RAB$M_KGE | RAB$M_WAT;
  itmrab.rab$b_rac = RAB$C_KEY;

  grprab.rab$l_kbf = (char *) &(newsitm.itm_grp);
  grprab.rab$b_ksz = 4;
  grprab.rab$b_krf = 1;
  grprab.rab$l_rop = RAB$M_WAT;
  grprab.rab$b_rac = RAB$C_KEY;

  if (!sys_get_nornf(&itmrab)) return(ret_val);
  if (strcmp(mref,newsitm.itm_id)) {
    sys_free(&itmrab);
    return(ret_val);
    }
  if (!sys_get_nornf(&grprab)) *(newsgrp.grp_name) = '\0';

  if (!sender) {
    if (no_confirm) del_loop = 1;
    else {
      if (smg_active)
        sprintf(err_oline,"Delete Item %.*s:%d? [n]:",
                GRPNAMELEN_F,newsgrp.grp_name, newsitm.itm_num);
      else {
        printf("\nNewsitem to delete:\n");
        printf("\tNewsgroup: %.*s\n",GRPNAMELEN_F,newsgrp.grp_name);
        printf("\t#        : %d\n",newsitm.itm_num);
        printf("\tTitle    : %s\n",newsitm.itm_title);
        }
      status = get_input(&usr_inp_dsc,c$dscl(&err_oline_d),&usr_inp_l);
      if (status == RMS$_EOF) {
        if (*(newsgrp.grp_name)) sys_free(&grprab);
        sys_free(&itmrab);
        return(ret_val);
        }
      if ((status & 1) && usr_inp_l && (tolower(*usr_inp) == 'y')) del_loop = 1;
      else {
        if (*(newsgrp.grp_name)) sys_free(&grprab);
        sys_free(&itmrab);
        return(ret_val);
        }
      }
    }
  else if (sender == (char *) 1) del_loop = 1;
  else if (*(newsgrp.grp_name)) {
    FILE *fp;
    char inpline[512],*cp, *cp1;

    fp = do_oitem(&newsitm,"r",util_grp_name_str(newsgrp.grp_name),0,
                  (newsgrp.grp_flags & NEWS_M_RESTRICT_SET),0);
    if (fp != 0) {
      while (fgets(inpline,sizeof(inpline),fp)) {
        if (*inpline == '\n') break;
        if (!strncmp(inpline,"From:",5) || !strncmp(inpline,"Sender:",7)) {
          if ((cp = strchr(inpline,'<')) != 0) {
            ++cp;
            chop_str(cp,'>');
            }
          else {
            cp = strchr(inpline,':');
            cp++;
            while (isspace(*cp)) cp++;
            cp1 = cp;
            while ((*cp1) && (!isspace(*cp1))) cp1++;
            *cp1 = '\0';
            chop_str(cp,'(');
            }
          if (!strcmp(sender,cp)) {
            del_loop = 1;
            break;
            }
          }
        }
      fclose(fp);
      }
    }
  if (!del_loop) {
    if (*(newsgrp.grp_name)) sys_free(&grprab);
    sys_free(&itmrab);
    return(ret_val);
    }
  while (del_loop) {
    /* now delete all items with this id - stop if the delete call fails */
    itmrab.rab$l_kbf = mref;
    itmrab.rab$b_ksz = IDLEN + 4;
    itmrab.rab$b_krf = 1;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_WAT;
    itmrab.rab$b_rac = RAB$C_KEY;

    grprab.rab$l_kbf = (char *) &(newsitm.itm_grp);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    if (!sys_get_nornf(&itmrab)) {
      if (*(newsgrp.grp_name)) sys_free_nornl(&grprab);
      return(ret_val);
      }
    if (strcmp(mref,newsitm.itm_id)) {
      if (*(newsgrp.grp_name)) sys_free_nornl(&grprab);
      sys_free(&itmrab);
      return(ret_val);
      }
    if (!sys_get_nornf(&grprab)) *(newsgrp.grp_name) = '\0';
    if (!sys_delete(&itmrab)) del_loop = 2; else del_loop = 1;
    ++ret_val;
    hist_add(newsitm.itm_id);
    if (*(newsgrp.grp_name)) {
      news_delete_file(newsitm.itm_num,util_grp_name_str(newsgrp.grp_name));
      }
    si = newsitm.itm_num;
    sg = newsitm.itm_grp;
    if (*(newsgrp.grp_name)) {
      if (newsgrp.grp_count > 0) --newsgrp.grp_count;
      ikey[0] = 0;
      ikey[1] = newsgrp.grp_num;
      itmrab.rab$l_kbf = (char *) &ikey[0];
      itmrab.rab$b_rac = RAB$C_KEY;
      itmrab.rab$b_ksz = 8;
      itmrab.rab$b_krf = 0;
      itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
      if (!sys_get_nornf(&itmrab) || (newsitm.itm_grp != newsgrp.grp_num))
        newsgrp.grp_firstnum = newsgrp.grp_topnum + 1;
      else newsgrp.grp_firstnum = newsitm.itm_num;
      sys_free_nornl(&itmrab);
      sys_update(&grprab);
      }
    if ((g = ga_locate(sg)) != 0) {
      if (ga[g]->grp_ia)
        if (find_itm_by_num(g,si,&i)) delitm_mem(g,si);
      else {
        if (ga[g]->grp_count > 0) --ga[g]->grp_count;
        if (!ga[g]->grp_count) {
          if (ga[g]->grp_iavdsize) {
            _c$cks(smg$erase_display(&(ga[g]->grp_iavd)));
            smg_put_chars(ga[g]->grp_iavd,"NEWSGROUP is empty",1,20,0,SMG$M_REVERSE);
            if (ga[g]->grp_iapaste != 4) {
              if ((news_context > 1) && (smg_active) && (curr_g == g))
                _c$cks(smg$move_virtual_display(&(ga[g]->grp_iavd),&pid,c$ac(4),c$rfi(1),0));
              ga[g]->grp_iapaste = 4;
              }
            }
          ga[g]->grp_c_itm = 0;
          if ((news_context > 1) && (curr_g == g)) curr_i = 0;
          }
        if (ga[g]->grp_unread) --ga[g]->grp_unread;
        if ((smg_active) && (ga[g]->grp_display_indx)) {
          sprintf(err_oline,"%5d",ga[g]->grp_count);
          smg_put_chars(grp_vd,err_oline,ga[g]->grp_display_indx,14+displ_subjlen,0,0);
          }
        screen_update_gread(g);
        }
      }
    if (del_loop == 2) {
      if (*(newsgrp.grp_name)) sys_free(&grprab);
      sys_free(&itmrab);
      return(ret_val);
      }
    }
  return 0;
}
#endif

static int ddi_1()
{
  int status;
  char s[80];
  unsigned short s_len;
  $DESCRIPTOR(s_dsc,s);
  int di,
      li,
      fi;

  no_confirm = (cli$present(c$dsc("CONFIRM")) == CLI$_NEGATED);
  if (cli$present(c$dsc("IDENTIFIER")) & 1) {
        /* note that del/id cannot pick up mail items */
    if (nntp_client) {
      err_line("Error: NNTP Client cannot Delete with Message ID - use CANCEL");
      return(0);
      }
    if (no_priv()) return(err_line("Error: Delete - No privilege"),0);
    if (!(cli$get_value(c$dsc("IDENTIFIER"),&s_dsc,&s_len) & 1))
      get_input(&s_dsc,c$dsc("Message-Id: "),&s_len);
    if (!s_len) return(0);
    s[s_len] = '\0';
    del_id(s,0);
    if (smg_active) newsgroup_header();
    return(0);
    }

  if (nntp_client && !(ga[curr_g]->grp_flags & NEWS_M_MAILGROUP)) {
    err_line("Error: NNTP Client cannot DELETE news items - use CANCEL");
    return(0);
    }
  if (no_priv() && !(ga[curr_g]->grp_flags & NEWS_M_MAILGROUP)) {
    char *ma;
    int s_l;

    strnncpy(s,sizeof(s), usr_username,-1, &s_l);
    strnncat(s,sizeof(s),s_l, "@",1, &s_l);
    strnncat(s,sizeof(s),s_l, Node_address,-1, &s_l);
    ma = moderator_address(ga[curr_g]->grp_name);
    if (!ma ||  (strcmp(s,ma)
                && (!(ga[curr_g]->grp_flags & NEWS_M_MOD_ACCESS))))
      return(err_line("Error: Delete - No privilege"),0);
    }

  d_itm[0].fitm = 0;
  if ((curr_i > 0) && (curr_g)) d_itm[0].fitm = ga[curr_g]->grp_ia[curr_i].itm_num;
  d_itm[0].litm = 0;
  d_itm[1].fitm = 0;

  if (cli$get_value(c$dsc("ITEM"),&s_dsc,&s_len) & 1) {
    char temp_s[256];
    int temp_s_l;

    temp_s_l = 0; temp_s[temp_s_l] = '\0';
    do {
      s[s_len] = '\0';
      strnncat(temp_s,sizeof(temp_s),temp_s_l, s,s_len, &temp_s_l);
      strnncat(temp_s,sizeof(temp_s),temp_s_l, ",",1,   &temp_s_l);
      } while (cli$get_value(c$dsc("ITEM"),&s_dsc,&s_len) & 1);
    if (temp_s_l>0) temp_s[--temp_s_l] = '\0';
    strnncpy(s,sizeof(s), temp_s,temp_s_l, 0);
    parse_items(temp_s,d_itm,curr_g,curr_i);
    }

  if ((!d_itm[0].fitm) || (!ga[curr_g]->grp_ia))
    return(err_line("Delete - Cannot locate newsitem"),0);
  strcpy(err_oline,"Delete Newsitem? [n]:");
  for (di = 0; (li = d_itm[di].litm), (fi = d_itm[di].fitm) ; ++di) {
    if (!no_confirm) {
      if ((fi > 0) && (!li)) {
        if (smg_active) sprintf(err_oline,"Delete Newsitem #%d? [n]:",fi);
        else {
	  printf("\nDelete:\n");
          printf("\tGroup: %s\n",ga[curr_g]->grp_name);
          printf("\t#        : %d\n",fi);
          }
        }
      else if (fi < 0) {
        if (smg_active) sprintf(err_oline,"Delete ALL Newsitems? [n]:");
	else {
	  printf("\nDelete:\n");
          printf("\tNewsgroup: %s\n",ga[curr_g]->grp_name);
          printf("\tALL ITEMS\n");
          }
        }
      else {
        if (smg_active)
          sprintf(err_oline,"Delete Newsitems #%d-%d? [n]:",fi,li);
        else {
          printf("\nNewsitem to delete:\n");
          printf("\tNewsgroup: %s\n",ga[curr_g]->grp_name);
          printf("\t#        : %d - %d\n",fi,li);
          }
        }
      status = get_input(&usr_inp_dsc,c$dscl(&err_oline_d),&usr_inp_l);
      if (status == RMS$_EOF) return(0);
      }
    if (   no_confirm
        || ((status & 1) && usr_inp_l && (tolower(*usr_inp)== 'y'))) {
      if ((fi > 0) && !li) delitm_file(curr_g,fi);
      else if (fi < 0) {
        set_level(2);
        li = ga[curr_g]->grp_count;
        for (fi = 1; fi <= li; fi++)
          delitm_file(curr_g,ga[curr_g]->grp_ia[1].itm_num);
        }
      else for ( ; fi<=li ; ++fi) delitm_file(curr_g,fi);
      }
    }
  if (smg_active) newsgroup_header();
  return(0);
}

int do_ditem()
{
  return(unwind_display(OUTER_LOOP,ddi_1));
}

/*
 *  do_dgroup
 *
 *  delete a news newsgroup
 */

static int ddg_1()
{
  char ngroup[GRPNAMELEN];
  unsigned short ngroup_len = 0;
  $DESCRIPTOR(ngroup_dsc,ngroup);
  int g, dgr = 1;

  if (nntp_client) {
    err_line("Error: NNTP Client cannot Delete newsgroups");
    return(0);
    }

  if (no_priv()) return(err_line("Error: Delete - No privilege"),0);
  no_confirm = (cli$present(c$dsc("CONFIRM")) == CLI$_NEGATED);
  if (!(cli$get_value(c$dsc("GROUP"),&ngroup_dsc,&ngroup_len) & 1))  {
    if ((g = curr_g) != 0) {
      del_grp(g);
      return(0);
      }
    }
  do {
    ngroup[ngroup_len] = '\0';
    util_cvrt(ngroup,ngroup);
    if (strchr(ngroup,'*') || strchr(ngroup,'%')) {
      for (g = 1; g <= ga_size; ++g) {
        if (   (wild_match(ga[g]->grp_name,ngroup))
            && (dgr = del_grp(g))) {
          if (dgr < 0) break;
          --g;
          }
        }
      }
    else if ((g = ga_exact_name(ngroup)) != 0) dgr = del_grp(g);
    } while (   (dgr >= 0)
             && (cli$get_value(c$dsc("GROUP"),&ngroup_dsc,&ngroup_len) & 1));
  return(0);
}

int
do_dgroup()
{
  return(unwind_display(OUTER_LOOP,ddg_1));
}

/*
 *  do_dcontext
 *
 *  delete either the current newsgroup or newsitem depending on current
 *  context level
 */

int
do_dcontext()
{
  if (no_priv()) return(err_line("Error: Delete - No privilege"),0);

  if ((news_context == 1) && curr_g) return(do_dgroup());
  if (cli$present(c$dsc("IDENTIFIER")) & 1) return (do_ditem());
  if ((news_context > 1) && curr_g && (curr_i > 0)) return(do_ditem());
  return(0);
}

/*
 *  do_cancel
 *
 *  CANCEL a news item - delete the local copy, and also send a cancel
 *  control news item through the net.
 */

static int dcncl()
{
  int status;
  time_t cur_time;
  char inpline[256], inpline_lc[256],
       mail_from[256], mail_cfrom[256], mail_groups[256],
       mail_dist[256], mail_id[256],
       post_path[256], post_dist[256],
       id[256], ngroup[256], loc_id[IDLEN],
       *p, *m_a;
  int inpline_l; /* strlen(inpline) */
  struct tm *stm;

  if (news_readonly) return(0);
                            /* preconditions - must be positioned on the item to cancel */
  if (!((curr_g) && (curr_i) && (news_context > 1))) {
    err_line("Error: Cancel - No current item to cancel");
    return(0);
    }
  if (ga[curr_g]->grp_flags & NEWS_M_MAILGROUP) {
    err_line("Error: Cancel - Cannot cancel mail items");
    return(0);
    }
                            /* check if - local posting and user is newsmgr or
                               local posting by this user or is the moderator
                               of the newsgroup. Get the From: field for the
                               check */
  if (!(fp = do_open_item(curr_g,curr_i,"r",&fp_open))) {
    err_line("Error: Cancel - Cannot access item text");
    return(0);
    }

  *mail_from = *mail_groups = *mail_dist = *mail_id = *id = '\0';
  while (fgets(inpline,sizeof(inpline),fp)) {
    strcpy(inpline_lc,inpline);
    lower_case(inpline_lc);
    if (*inpline == '\n') break;
    if (!strncmp(inpline_lc,"from:",5))             strcpy(mail_from,  inpline);
    else if (!strncmp(inpline_lc,"newsgroups:",11)) strcpy(mail_groups,inpline);
    else if (!strncmp(inpline_lc,"distribution:",13)) strcpy(mail_dist,inpline);
    else if (!strncmp(inpline_lc,"message-id:",11))   strcpy(mail_id,  inpline);
    }
  fclose(fp);
/*if (*fp_open > 1) delete_file_versions(fp_open); */
  fp_open = 0;

  for ( p=mail_id; *p != ':'; ++p )
      *p = tolower(*p);
  sscanf(mail_id,"message-id: %s",id);

  if ((!*mail_from) || (!*mail_groups) || (!*id)) {
    err_line("Error: Cancel - Cannot establish author of this item");
    return(0);
    }

  strcpy(mail_cfrom,mail_from);
  lower_case(mail_cfrom);
  if ((m_a = moderator_address(ga[curr_g]->grp_name)) != 0) lower_case(m_a);
  strnncpy(inpline,sizeof(inpline), mail_sig,-1, &inpline_l);
  lower_case(inpline);
  if (!m_a || strcmp(inpline,m_a)) {
    inpline_l = sprintf(inpline,"*%.200s@",usr_username);
    strnncat(inpline,sizeof(inpline),inpline_l, Node_address,-1, &inpline_l);
    strnncat(inpline,sizeof(inpline),inpline_l, "*",1, &inpline_l);
    lower_case(inpline);
    if (!(wild_match(mail_cfrom,inpline))) {
      inpline_l = sprintf(inpline,"*%.200s*",mail_sig);
      lower_case(inpline);
      if (!(wild_match(mail_cfrom,inpline))) {
        if (no_priv() && !(ga[curr_g]->grp_flags & NEWS_M_MOD_ACCESS)) {
          err_line("Error: Cancel - Only original poster may cancel items");
 	  return(0);
          }
        else {
          inpline_l = sprintf(inpline,"*@%.200s*\n",Node_address);
          lower_case(inpline);
          if (!(wild_match(mail_cfrom,inpline))) {
            err_line("Error: Cancel - Item was not posted from this node");
            return(0);
            }
          }
        }
      }
    }

  sprintf(err_oline,"CANCEL item #%d? [n]:",ga[curr_g]->grp_ia[curr_i].itm_num);
  status = get_input(&usr_inp_dsc,c$dscl(&err_oline_d),&usr_inp_l);
  if (status == RMS$_EOF) return(0);
  if ((status & 1) && (usr_inp_l) && (tolower(*usr_inp) == 'y')) {
    if ((fp = fopen(Post_file,"w")) != 0) {
      int post_path_l;
      char subj_line[132];

      fprintf(fp,"Path: %s!%s\n",news_pathname,usr_username);
      strcpy(post_path," "); post_path_l = 1;
      strnncat(post_path,sizeof(post_path),post_path_l, news_pathname,-1, &post_path_l);
      strnncat(post_path,sizeof(post_path),post_path_l, "!",1,            &post_path_l);
      strnncat(post_path,sizeof(post_path),post_path_l, usr_username,-1,  &post_path_l);
      fprintf(fp,mail_from);
      sscanf(mail_groups,"Newsgroups: %s",ngroup);
      fprintf(fp,mail_groups);
      fprintf(fp,"Subject: cancel %s\n",id);
      strcpy(subj_line,"cancel ");
      strnncat(subj_line,sizeof(subj_line),-1, id,-1, 0);
      strnncpy(loc_id,sizeof(loc_id), gen_id(),-1, 0);
      fprintf(fp,"Message-ID: %s\n",loc_id);
      time(&cur_time);
      p = ctime(&cur_time);
      p += 4;
      stm = localtime(&cur_time);
      fprintf(fp,"Date: %d %.3s %04d %02d:%02d:%02d %s\n",
              stm->tm_mday, p, stm->tm_year + 1900,
              stm->tm_hour, stm->tm_min, stm->tm_sec, news_timezone);
      fprintf(fp,"Control: cancel %s\n",id);
      *post_dist = '\0';
      if (*mail_dist) {
        if (!sscanf(mail_dist,"Distribution: %s",post_dist))
        *post_dist = '\0';
        fprintf(fp,mail_dist);
        }
      fprintf(fp,"Lines: 1\n\ncancel %s\n",id);
      fclose(fp);
      if (nntp_client) post_to_server(nntp_node,nntp_proto,Post_file);
#if !NNTP_CLIENT_ONLY
      else {
        unsigned int cre_grp[2];
        char *cp;

        chop_str(mail_from,'\n');
        cp = &mail_from[6];
        while (isspace(*cp)) cp++;
        do_new_group("control",0,cre_grp);
        if (cre_grp[0])
          do_new_item(cre_grp,loc_id,subj_line,cp,Post_file,1,1,1,0);
        sys_remote_send(post_path,ngroup,post_dist,Post_file,loc_id,0);
	flush_downstream(0);
        }
#endif
      delete_file_versions(Post_file);
      }
    del_id(ga[curr_g]->grp_ia[curr_i].itm_id, (char *) 1);
    if (smg_active) newsgroup_header();
    }
  return(0);
}

int do_cancel()
{
  return(unwind_display(OUTER_LOOP,dcncl));
}
