/**
**
**  MODIFICATION HISTORY:
**	V6.1b7  15-May-1993     Charles Bailey  bailey@genetics.upenn.edu
**	  - optimize item number lookup
**	  - change update_newsgroup() to update grp_c_itm always
**	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 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace several calls to strcpy/strcat with safer strnncpy/strnncat
**	  - replace pattern strcpy(news_malloc(strlen(x)+1),x) with news_strdup
**	  - replace pattern strcat(err_oline,x) with err_oline_cat(x)
**	  - change %s to %.<n>s in sprintf to guard against overwriting buffer
** --
**/

#ifdef vaxc
#module NEWSMOD "V6.2"
#endif

#define _NEWSMOD_C
#define module_name "NEWSMOD"

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


/*
 *  do_attach
 *
 *  Attach to a process
 */

int do_attach()
{
  int status;
  char p[132];
  unsigned short p_len;
  $DESCRIPTOR(p_dsc,p);
  int ppid, save_displ;

  if (cli$get_value(c$dsc("PROCESSNAME"),&p_dsc,&p_len) & 1) {
    p[p_len] = '\0';
    if (!(lib$getjpi(c$ac(JPI$_PID),0,c$dsc(p),&ppid,0,0) & 1)) ppid = 0;
    }
  else {
    ppid = getppid();
    strcpy(p,"[Parent]");
    }
  if (!ppid) {
    err_line("Attach: invalid process");
    return(0);
    }
  save_displ = leave_screen(1);
  printf("\nNews: Attaching to process %s ...\n",p);
  lib$attach(&ppid);
  join_screen(save_displ);
#if !MEM_DYNAMIC_QUOTA
  _c$cks(set_mem_ceiling());
#endif
  return(0);
}

/*
 *  d o _ d e f i n e
 *
 *  Fill out a key definition into the local keytable
 */

int do_define()
{
  int status;

  if (!((status = smg$define_key(&keytab,c$dsc(cmd))) & 1)) {
    sprintf(err_oline,"Error: Define/Key returned error (code = %X)",status);
    err_line(err_oline);
    }
  return(0);
}

/*
 *  do_setaccess
 *
 *  Enter the ACL editor to set access to a newsgroup
 */

int do_setaccess()
{
#if !NNTP_CLIENT_ONLY
  char ngroup[132], itm_fname[256], *cp;
  int save_displ;
  unsigned short ngroup_len = 0;
  $DESCRIPTOR(ngroup_dsc,ngroup);

  if (nntp_client) return(0);
  if (no_priv()) return(err_line("Error: SET ACCESS - No privilege for operation"),0);
  if (cli$get_value(c$dsc("NEWSGROUP"),&ngroup_dsc,&ngroup_len) == CLI$_ABSENT)
    get_input_dflt(&ngroup_dsc,c$dsc("Newsgroup: "),&ngroup_len,
                    (curr_g ? c$dsc(ga[curr_g]->grp_name) : 0),0);
  ngroup[ngroup_len] ='\0';
  sprintf(itm_fname,Dir_template,util_dir(ngroup));
  cp = strrchr(itm_fname,'.');
  *cp = ']';
  cp = strchr(itm_fname,'^');
  *cp = '.';
  if (!access(itm_fname,0)) {
    save_displ = leave_screen(0);
    strcpy(ngroup,"EDIT/ACL ");
    strnncat(ngroup,sizeof(ngroup),-1, itm_fname,-1, 0);
    lib$spawn(c$dsc(ngroup),0,0,0,0,0,0,0,0,0,0,0,0);
    join_screen(save_displ);
    }
  /* This should check for the presence of a resultant ACL on the directory.
     if exists then now comes the tricky bit - get all newsgroups which match ngroup[.*]
     and set them as /ACCESSRESTRICT

     as well as this there should be a /NOACCESSRESTRICT which removes
     the flag and removes the ACL - this sould also be called when
     the above check for an ACL fails
   */
#endif

  return(0);
}

/*
 *  do_spawn
 *
 *  Spawn a subprocess
 */

int do_spawn()
{
  int status;
  int save_displ, flags = 0;
  unsigned short cm_l, ip_l, op_l, pr_l;
  char cm[256], ip[256], op[256], pr[132];
  struct dsc$descriptor_const_s *cmd = 0, *ipd = 0, *opd = 0, *prd = 0;
  $DESCRIPTOR(cm_dsc,cm);
  $DESCRIPTOR(ip_dsc,ip);
  $DESCRIPTOR(op_dsc,op);
  $DESCRIPTOR(pr_dsc,pr);

  if (captive_account())
    return(err_line("Error: Cannot SPAWN from a Captive account"),0);

  if (cli$get_value(c$dsc("COMMAND"),&cm_dsc,&cm_l) & 1) {
    cm_dsc.dsc$w_length = cm_l;
    cmd = (struct dsc$descriptor_const_s *) &cm_dsc;
    }
  if (cli$get_value(c$dsc("INPUT"),&ip_dsc,&ip_l) & 1) {
    ip_dsc.dsc$w_length = ip_l;
    ipd = (struct dsc$descriptor_const_s *) &ip_dsc;
    }
  if (cli$get_value(c$dsc("OUTPUT"),&op_dsc,&op_l) & 1) {
    op_dsc.dsc$w_length = op_l;
    opd = (struct dsc$descriptor_const_s *) &op_dsc;
    }
  if (cli$get_value(c$dsc("PROCESS"),&pr_dsc,&pr_l) & 1) {
    pr_dsc.dsc$w_length = pr_l;
    prd = (struct dsc$descriptor_const_s *) &pr_dsc;
    }
  if (cli$present(c$dsc("LOGICAL_NAMES")) == CLI$_NEGATED)
    flags |= CLI$M_NOLOGNAM;
  if (cli$present(c$dsc("SYMBOLS")) == CLI$_NEGATED)
    flags |= CLI$M_NOCLISYM;
  if (cli$present(c$dsc("WAIT")) == CLI$_NEGATED)
    flags |= (CLI$M_NOWAIT | CLI$M_NOTIFY);

  save_displ = leave_screen(0);
  printf("\nSpawning subprocess\n");
  lib$spawn(cmd,ipd,opd,&flags,prd,0,0,0,0,0,0,0,0);
  if ((cmd || ipd) && !opd) {
    printf("\n<Return> to continue NEWS");
    gets(cm);
    }
  join_screen(save_displ);
#if !MEM_DYNAMIC_QUOTA
  _c$cks(set_mem_ceiling());
#endif
  return(0);
}

/*
 *  do_start_version
 *
 *  Display NEWS Version
 */

int do_start_version()
{
  sprintf(err_oline,"%.200s (%.200s)",NEWS_VERSION,NEWS_VDATE);
  if (!no_priv()) err_oline_cat("      [NewsManager Access Enabled]");
  err_line(err_oline);
  return(0);
}

/*
 *  add_to_mod
 *  read_mod_file
 *  moderator_address
 *  add_moderator
 *
 *  Use mailpaths file to resolve the mailpath address
 */

#if NNTP_CLIENT_ONLY

char *moderator_address(newsgroup) char *newsgroup; { return(0); }
int check_moderator(address) char *address; { return(0); }
int add_moderator(name,address) char *name, *address; { return(0); }

#else

static
struct mail_list {
  char *group,
       *mod_address;
  struct mail_list *mod_next;
  } *mailpaths = 0;

static
char mail_rtn[256];

static void
clear_mailpaths()
{
  struct mail_list *next;

  for (; mailpaths; mailpaths=next) {
    next = mailpaths->mod_next;
    news_free(mailpaths->group);
    news_free(mailpaths->mod_address);
    news_free(mailpaths);
    }
}

static void add_tail_mod(name,address)
  char *name, *address;
{
  struct mail_list *h, *t;

  if (!mailpaths) {
    mailpaths = (struct mail_list *) news_malloc(sizeof *mailpaths);
    mailpaths->group = news_strdup(name,-1);
    mailpaths->mod_address = news_strdup(address,-1);
    mailpaths->mod_next = 0;
    return;
    }
  for (h=mailpaths; h->mod_next; h=h->mod_next) {}
  t = (struct mail_list *) news_malloc(sizeof *mailpaths);
  t->group = news_strdup(name,-1);
  t->mod_address = news_strdup(address,-1);
  t->mod_next = 0;
  h->mod_next = t;
}

/*
 *  read_mod_file
 *
 *  Read the mailpaths file into a mem structure
 */

static void read_mod_file(force)
  int force;
{
  static int s_mod = 0,
             s_check_time = 0;
  struct stat s_stat;
  int st;
  FILE *fp;
  char *in_line, *cp3, *np1,
       name[256], address[256];

  if (force) s_mod = 0;
  if (!s_mod || (s_check_time < time(0))) {
    sysprv();
    s_check_time = (time(0) + RECHECK_TIMER);
    st = stat(MAILPATHS_FILE,&s_stat);
    if (st || (s_stat.st_mtime != s_mod)) {
      s_mod = 1;
      clear_mailpaths();
      if ((fp = fopen(MAILPATHS_FILE,"r")) != 0) {
	while ((in_line = fgetl(fp)) != 0) {
	  chop_str(in_line,'#');
	  chop_str(in_line,'\n');
	  strip_compress_lower(in_line);
	  if (!strlen(in_line)) continue;
	  if (sscanf(in_line,"%s %s",name,address) == 2) {
            np1 = name;
            for (cp3=name; *cp3; ) {
              if (   !strncmp(cp3,"all",3)
                  && ((*(cp3 + 3) == '.') || !(*(cp3 + 3)))
                  && ((cp3 == name) || (*(cp3 - 1) == '.'))) {
                *np1++ = '*';
                cp3 += 3;
	        }
              else *np1++ = *cp3++;
              }
            *np1 = *cp3;
            if (!strcmp(name,"backbone")) strcpy(name,"*");
            else if (!strcmp(name,"internet")) continue;
            add_tail_mod(name,address);
            }
          }
        }
      fclose(fp);
      s_mod = s_stat.st_mtime;
      }
    nosysprv();
    }
}

/*
 *  moderator_address
 *
 *  Returns the address of the moderator of a nominated newsgroup
 */

char *moderator_address(newsgroup)
  char *newsgroup;
{
  char lg[132], *p;
  struct mail_list *mhead;

  if (nntp_client) return(0);
  *mail_rtn = '\0';
  read_mod_file(0);
  for (mhead=mailpaths; mhead; mhead=mhead->mod_next) {
    sprintf(err_oline,"%.500s.*",mhead->group);
    if (wild_match(newsgroup,mhead->group) || wild_match(newsgroup,err_oline)) {
      strnncpy(lg,sizeof(lg), newsgroup,-1, 0);
      for (p=lg; *p; p++)
        { if (*p == '.') *p = '-'; }
      sprintf(mail_rtn,mhead->mod_address,lg);
      return(mail_rtn);
      }
    }
  return(mail_rtn);
}

/*
 *  check_moderator
 *
 *  Check if the supplied address moderates any newsgroups
 */

int check_moderator(address)
    char *address;
{
  char la[256],
       ma[256];
  int g;
  struct mail_list *mhead;

  if (nntp_client) return(0);
  strnncpy(la,sizeof(la), address,-1, 0);
  lower_case(la);
  read_mod_file(0);
  if (!mailpaths) return(0);
  for (mhead = mailpaths; mhead; mhead = mhead->mod_next) {
    strnncpy(ma,sizeof(ma), mhead->mod_address,-1, 0);
    lower_case(ma);
    if (!strcmp(ma,la)) return(1);
    }
  for (g = 1; g <= ga_size; ++g)
    if (ga[g]->grp_flags & NEWS_M_MOD_ACCESS) return(1);
  return(0);
}

/*
 *  add_moderator
 *
 *  Add moderator address to mailpaths file
 */

int
add_moderator(name,address)
  char *name, *address;
{
  FILE *fpr,
       *fpw;
  char lg[132],
       *in_line,
       *tmpstr = 0,
       lm[256],
       na[256],
       *p1,
       *p2;
  int sm = 0, sl;

  if (nntp_client) return(0);
  p1 = lm;
  for (p2=address; *p2; p2++) {
    *p1++ = *p2;
    if (*p2 == '%') *p1++ = '%';
    }
  *p1 = '\0';

  strnncpy(lg,sizeof(lg), MAILPATHS_FILE,-1, 0);
  sysprv();
  fpr = fopen(lg,"r");
  if (!(fpw = fopen(lg,"w"))) {
    if (fpr) fclose(fpr);
    nosysprv();
    return(0);
    }

  fprintf(fpw,"%s %s\n",name,lm);
  if (fpr) {
    while ((in_line = fgetl(fpr)) != 0) {
      sl = strlen(in_line);
      if (sl >= sm) {
        if (sm) news_free(tmpstr);
        tmpstr = news_malloc(sm = sl+1);
        }
      strnncpy(tmpstr,sizeof(tmpstr), in_line,sl, 0);
      chop_str(in_line,'#');
      chop_str(in_line,'\n');
      strip_compress_lower(in_line);
      if (   !strlen(in_line)
	  || (sscanf(in_line,"%s %s",na,lm) != 2)
          || strcmp(name,na))
        fputs(tmpstr,fpw);
      }
    if (sm) news_free(tmpstr);
    }
  fclose(fpw);
  strnncat(lg,sizeof(lg),-1, ".;-1",4, 0);
  delete_file_versions(lg);
  nosysprv();
  read_mod_file(1);
  return(1);
}
#endif

/*
 *  write_access
 *
 *  Return 1 if a user may post to the specified newsgroup
 */

int gwrite_access(g)
  int g;
{
  if (!(ga[g]->grp_flags & NEWS_M_ACCESS_CHECKED)) check_access(g);
  if (!(ga[g]->grp_flags & NEWS_M_WRITE_ACCESS)) return(0);
  if (ga[g]->grp_flags & NEWS_M_LOCAL) return(1);
  if (!nout_priv()) return(g);
  ga[g]->grp_flags &= ~(NEWS_M_WRITE_ACCESS | NEWS_M_MOD_ACCESS | NEWS_M_MOD_USER);
  return(0);
}

int write_access(s)
  char *s;
{
  int g;

                    /* unrestricted user - post anywhere ! */
  if (!no_priv()) return(1);
  if ((g = ga_exact_name(s)) != 0) return(gwrite_access(g));
  return(0);
}

/*
 *  auth_list
 *
 *  Check a newsgroup list, and edit out unauthorized entries
 */

int auth_list(gl)
  char *gl;
{
  char s[GRPNAMELEN],
       *locg,
       *cp1,
       *cp2 = gl,
       *cp3;

  locg = news_strdup(gl,-1);
  for (cp1=locg;  (cp3 = chop_str_plus(cp1,',')) != 0;  cp1=cp3) {
    util_cvrt(s,cp1);
    if (write_access(s)) {
      strcpy(cp2,s);
      cp2 += strlen(cp2);
      *cp2++ = ',';
      }
    }
  util_cvrt(s,cp1);
  if (write_access(s)) {
    strcpy(cp2,s);
    cp2 += strlen(cp2);
    }
  if (cp2 != gl) {
    cp3 = cp2;
    if (*--cp3 == ',') cp2 = cp3;
    }
  *cp2 = '\0';
  news_free(locg);
  return(strlen(gl));
}

static int save_updg;

int do_update()
{
  int g;
  char en[256];
  unsigned short en_len;
  $DESCRIPTOR(en_dsc,en);

  if (cli$present(c$dsc("CLIENT")) & 1)
    return(do_update_client());
  if (cli$get_value(c$dsc("ENTRYNAME"),&en_dsc,&en_len) & 1) {
    en[en_len] = '\0';
    lower_case(en);
    if ((g = ga_search_name(en)) != 0) {
      save_updg = g;
      return(unwind_display(OUTER_LOOP,update_newsgroup_1));
      }
    err_line("Error: Update - No such newsgroup located");
    return(0);
    }
  unwind_display(OUTER_LOOP,do_update_1);
  return(0);
}

int do_update_1()
{
  char s_gname[256];
  int s_inum = 0,
      cdt = cur_dir_type + 1,
      slevel=news_context,
      sg, si;

  strnncpy(s_gname,sizeof(s_gname), ga[curr_g]->grp_name,-1, 0);
  if (curr_i && ga[curr_g]->grp_ia)
    s_inum = ga[curr_g]->grp_ia[curr_i].itm_num;
  mem_reset(0,1);
  do_dir(cdt,1);
  if ((sg = ga_exact_name(s_gname)) != 0) {
    do_select("%",sg,slevel);
    map_items(sg);
    if (s_inum && find_itm_by_num(sg,s_inum,&si)) cur_set_itm(sg,si);
    }
  return(0);
}

static int update_newsgroup_1()
{
  return(update_newsgroup(save_updg));
}

static int update_newsgroup(g)
  int g;
{
  ITM_PTR l_ia;
  GRP_PTR l_ga;
  int slevel=news_context, sg = curr_g, l_itm, f_itm, i, s_inum = 0;
  char rl[2048], il[20];

  do_select("%",g,3);
  if (ga[g]->grp_ia) s_inum = ga[g]->grp_ia[ga[g]->grp_c_itm].itm_num;
  if (ga[g]->grp_flags & NEWS_M_MAILGROUP) update_mail(g);
  else {
    set_level(2);
    l_ga = ga[g];
    l_ia = l_ga->grp_ia;
    *rl = '\0';
    if (!l_ga->grp_count) sprintf(rl," <%d",l_ga->grp_topnum);
    else if (!l_ia) sprintf(rl," <0");
    else {
      f_itm = l_itm = 0;
      for (i=1; i <= l_ga->grp_count; i++) {
        if (l_ia[i].itm_flags & NEWS_M_UNREAD) {
          l_itm = l_ia[i].itm_num - 1;
          if (!f_itm) sprintf(il," <%d",l_itm);
          else if (f_itm != l_itm) sprintf(il," |%d=%d",f_itm,l_itm);
          else sprintf(il," %d",l_itm);
          strnncat(rl,sizeof(rl),-1, il,-1, 0);
          while ((i <= l_ga->grp_count) && (l_ia[i].itm_flags & NEWS_M_UNREAD))
            ++i;
          if (i > l_ga->grp_count) { i = 0; break; }
          else f_itm = l_ia[i].itm_num;
          }
        }
      if (i > l_ga->grp_count) {
        l_itm = l_ia[i-1].itm_num;
        if (!f_itm) sprintf(il," <%d\n",l_itm);
        else if (f_itm != l_itm) sprintf(il," |%d=%d\n",f_itm,l_itm);
        else sprintf(il," %d\n",l_itm);
        strnncat(rl,sizeof(rl),-1, il,-1, 0);
        }
      }
    if (l_ga->grp_reg_text) news_free(l_ga->grp_reg_text);
    l_ga->grp_reg_text = news_strdup(rl+1, -1);
    if (l_ga->grp_ia) news_free(l_ga->grp_ia);
    l_ga->grp_ia = 0;
    if (l_ga->grp_idtail) {
      while ((l_ga->grp_idtail = (l_ga->grp_idtail)->b_link))
        news_free((l_ga->grp_idtail)->f_link);
      news_free(l_ga->grp_idhead);
      l_ga->grp_idhead = 0;
      }
    l_ga->grp_flags &= ~NEWS_M_ITMIDSSET;
    if (l_ga->grp_iavdsize) smg$delete_virtual_display(&l_ga->grp_iavd);
    l_ga->grp_iavdsize = 0;
    set_level(3);
    }
  do_select("%",sg,slevel);
  if (s_inum && find_itm_by_num(g,s_inum,&i)) cur_set_itm(g,i);
  else cur_set_itm(g,min(ga[g]->grp_c_itm,ga[g]->grp_count));
  return(0);
}

int unwind_display(level,func)
  int level;
  int (*func)();
{
  if (parse_level == OUTER_LOOP) return((*func)());
  longjmp(((!smg_active) || (level == OUTER_LOOP) || (news_context < 3))
	  ? env : envdisp, (int) func);
  /*NOTREACHED*/
  return 0;
}
