/*
  tossing functions
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <dirent.h>

#include "config.h"

list_t *newareas_descs;
list_t *newareas_users;

int crc32_ignore; /* initializated from gtic.c */

void swap_uid_gid()
{
	setreuid(geteuid(),getuid());
	setregid(getegid(),getgid());
}

int check_replaces(char *name,char *replaces)
/* Check "Replaces" keyword from tic. Return TRUE when ok.
   Usually arguments is tic_t->file and tic_t->replaces.
*/
{
  char *n,*r;

  if(name==NULL || replaces==NULL) return FALSE;

  /* if we found ? or * in name, return FALSE */ 
  if(strchr(name,'?') || strchr(name,'*')) return FALSE;

  /* simple compare with ignoring case */
  if(strcasecmp(name,replaces) == 0) return TRUE;

  /* Strip extension of name and replaces and compare it. If extention not
     exist, return FALSE. If stripped name!=replaces, return FALSE.
     TODO: most right method need...
  */
  if(strchr(name,'.')==NULL || strchr(replaces,'.')==NULL) return FALSE;

  n=xstrcpy(name);
  r=xstrcpy(replaces);

  /* strip extension */
  strrchr(n,'.')[0]=0;
  strrchr(r,'.')[0]=0;

  if(strcasecmp(n,r)==0 && n[0]!=0 && r[0]!=0)
  {
    xfree(n);
    xfree(r);
    return TRUE;
  }

  xfree(n);
  xfree(r);

  return FALSE;
}

int validate_group(area_t *a,user_t *u)
/* Validate user_t groups for area_t. Returns TRUE when ok. */
{
  int i; char *cur_group;
  ass(a!=NULL);
  ass(u!=NULL);
  if(a->group==NULL) return TRUE; /* area group check disabled for this area */
  for(i=0;i<list_head_max(u->groups);i++)
  {
    cur_group=list_get_key(u->groups,i);
    if(cur_group[0]!='!') continue;
    if(!strcasecmp(a->group,cur_group+1)) return FALSE;
    if(wildmat_icase(a->group,cur_group+1)) return FALSE;
  }
  for(i=0;i<list_head_max(u->groups);i++)
  {
    cur_group=list_get_key(u->groups,i);
    if(cur_group[0]=='!') continue;
    if(!strcasecmp(a->group,cur_group)) return TRUE;
    if(wildmat_icase(a->group,cur_group)) return TRUE;
  }
  return FALSE;
}

static void process_gtic_q_dir(char *dirname)
/*
 * Process gtic_q directory. Add gtic_q\* to dirname\* when dirname\*.bsy not
 * found (sorry for backslashes ;-).
 * */
{
  DIR *dir; struct dirent *dirp; struct stat st_buf;
  char buff[PATH_MAX],ftmp[NAME_MAX],infile[PATH_MAX];
  strcpy(buff,dirname); strcat(buff,"/gtic_q");
  dir=opendir(buff);
  if(dir==NULL) return;
  while(1)
  {
    dirp=readdir(dir);
    if(dirp==NULL) break;
    strcpy(buff,dirname); strcat(buff,"/gtic_q/"); strcat(buff,dirp->d_name);
    if(stat(buff,&st_buf)) continue;
    if(!(st_buf.st_mode & S_IFREG)) continue;

    strcpy(ftmp,dirp->d_name);
    if(strchr(ftmp,'.')) strcpy(strrchr(ftmp,'.'),".bsy");
    strcpy(buff,dirname); strcat(buff,"/"); strcat(buff,ftmp);
    if(access_via_stat(buff,F_OK)==0) continue;	/* bsy found - skip */

    strcpy(buff,dirname);
    strcat(buff,"/");
    strcat(buff,dirp->d_name);

    strcpy(infile,dirname);
    strcat(infile,"/gtic_q/");
    strcat(infile,dirp->d_name);

    if(cp_add(infile,buff))
    {
      l_printf("unable to add %s to %s",infile,buff);
      continue;
    }
    l_printf("OK_GTIC_Q: %s",infile);
    remove(infile);
  }
  strcpy(buff,dirname); strcat(buff,"/gtic_q"); rmdir(buff);
  closedir(dir);
}

void process_gtic_q()
/* scan gtic_q directories in outbound and try to fix bsy'ed xlo's */
{
  char buff[PATH_MAX]; DIR *dir; struct dirent *dirp;
  struct stat stat_buf;
	int i;
	char *cur_outbound;
	for(i=0;i<ilist_head_max(domains);i++)
	{
		cur_outbound=ilist_get_entry(domains,i);
		ass(cur_outbound!=NULL);
		process_gtic_q_dir(cur_outbound);
		dir=opendir(cur_outbound);
		if(dir==NULL) continue;
		while(1)
		{
			dirp=readdir(dir);
			if(dirp==NULL) break;
			if(wildmat_icase(dirp->d_name,"*.pnt")==0) continue;
			snprintf(buff,sizeof(buff)-1,"%s/%s",cur_outbound,dirp->d_name);
			if(stat(buff,&stat_buf)) continue;
			if(!(stat_buf.st_mode & S_IFDIR)) continue;
			process_gtic_q_dir(buff);
		}
		closedir(dir);
	}
}

static int write_to_xlo(char *xlo,char *text,int kill,char *bsy,int mk_bsy)
{
  FILE *fp,*fp_bsy=NULL;
  char *p,prev=0;
  if(mk_bsy==TRUE)
    fp_bsy=fopen(bsy,"a");
  fp=fopen(xlo,"a");
  if(fp==NULL)
  {
    if(fp_bsy)
      ass(fclose(fp_bsy)==0);
    return 1;
  }
  p=text;
  if(text[0]=='^') { kill=1; p++; }
  if(kill==1) fputc('^',fp);
  while(*p)
  {
    if(*p == '/' && prev=='/') { p++; continue; }
    prev=*p;
    fputc(prev,fp);
    p++;
  }
  fputc('\n',fp);
/*  fprintf(fp,"%s%s\n",kill?"^":"",text); */
  ass(fclose(fp)==0);
  if(mk_bsy==TRUE)
  {
    if(fp_bsy)
      ass(fclose(fp_bsy)==0);
    remove(bsy);
  }
  return 0;
}

static void mk_badname(ticlist_t *tlcur)
/* make tlcur->badname for renaming .tic to .bad */
{
  char buff[PATH_MAX]; char *p;
  strcpy(buff,tlcur->name);
  p=strrchr(buff,'.');
  if(p) p[0]=0;
  strcat(buff,".bad");
  tlcur->badname=xstrcpy(buff);
}

void create_outbound(void)
/* create outbound: copy files if hardlinks==1, make xlo's */
{
  int i,j,k,mk_bsy,first_file; user_t *utmp; FILE *fp;
  char xlo[PATH_MAX],qdir[PATH_MAX],bsy[PATH_MAX],out_path[PATH_MAX];
  char previous_file[PATH_MAX],ftmp[PATH_MAX],xlo_str[PATH_MAX+1];
  char buff[BUFSIZE],in_path[PATH_MAX];
  int real_make_hardlinks; /* set to TRUE when area is "passthru" or
				  make_hardlinks==1 */
  tic_t *newtic; ticlist_t *tlcur; area_t *acur; tic_t *tic;
  char *p;
  for(i=0;i<ticlist_list_max;i++)
  {
    tlcur=ilist_find_entry(ticlist_list,i);
    ass(tlcur!=NULL);
    if(tlcur->bad==TRUE) continue;
    tic=tlcur->tic;
    ass(tic!=NULL);
    acur=list_find_entry(areas,tic->area);
    ass(acur!=NULL);
/* printf("DEBUG: list_head_max = %d\n",list_head_max(acur->users)); */
    first_file=TRUE;

    real_make_hardlinks=make_hardlinks;
    if(acur->flags & AREA_PASSTHRU) real_make_hardlinks=TRUE;
    
    for(j=0;j<list_head_max(acur->users);j++)
    {
      utmp=list_get_entry(acur->users,j);
      ass(utmp!=NULL);
/* printf("DEBUG: utmp=%p\n",utmp); */
      if(utmp->flags & USER_PAUSE) continue;

      if(nodecmp(&(utmp->addr),&(tic->from))==0) continue;
      if(nodecmp(&(utmp->addr),&(tic->origin))==0) continue;
      nodetoftn(ftmp,&(utmp->addr));
      if(list_find(tic->seenby,ftmp)) continue;

      /* check for area group */
      if(!validate_group(acur,utmp))
      {
        l_printf("SKIP: area=%s, group=%s - node %s don't have this group.",
	  acur->name,acur->group,ftmp);
	continue;
      }

/*
printf("DEBUG: ok (%d:%d/%d.%d, node %s, tic %s)\n",
(utmp->addr).zone,
(utmp->addr).net,
(utmp->addr).node,
(utmp->addr).point,
ftmp,tlcur->name);
*/

      /* make new tic */
      newtic=copytic(tic);

      /* from */
      if(acur->aka)
        nodecpy(&(newtic->from),acur->aka);
      else
        nodecpy(&(newtic->from),&whoami);

      /* to */
      nodecpy(&(newtic->to),&(utmp->addr));

      /* password */
      if(newtic->pw) xfree(newtic->pw);
      newtic->pw=xstrcpy(utmp->passwd);

      /* created by */
      if(newtic->created) xfree(newtic->created);
      newtic->created=xstrcpy(
       "by gtic " VERSION " Copyright (C) 1998 Yuri Kuzmenko 2:463/169"
                             );

      /* path, assumes newtic->path!=NULL */
      ass(newtic->path!=NULL);
      snprintf(buff,sizeof(buff)-1,"%s %lu %s",
       acur->aka?nodetoftn(NULL,acur->aka):nodetoftn(NULL,&whoami),
       (unsigned long)time(NULL),texttime(1));
      p=xstrcpy(buff);
      ilist_add(newtic->path,newtic->max_path++,p);

      /* seenby, assumes newtic->seenby!=NULL */
      ass(newtic->seenby!=NULL);
      for(k=0;k<list_head_max(acur->users);k++)
      {
        user_t *utmp;
        utmp=list_get_entry(acur->users,k);
	ass(utmp!=NULL);

	if((utmp->addr).point!=0 && add_point_seenby==0) continue;
	
        nodetoftn(ftmp,&(utmp->addr));
        if(list_find(newtic->seenby,ftmp)==NULL)
          list_add(newtic->seenby,ftmp,"");
      }

      /* kill magic, from_pwd (by FSC-0087) */
      if(newtic->magic)    { xfree(newtic->magic);    newtic->magic=NULL; }
      if(newtic->from_pwd) { xfree(newtic->from_pwd); newtic->from_pwd=NULL; }

      /* if areadesc absent in tic and area has description, add it */
      if(newtic->areadesc==NULL && acur->desc!=NULL)
        newtic->areadesc=xstrcpy(acur->desc);

      /* make base of outbound file name */
#ifndef AMIGA4D
      if(utmp->addr.point!=0)
        snprintf(ftmp,sizeof(ftmp)-1,"%04x%04x.pnt/%08x",
         utmp->addr.net,utmp->addr.node,utmp->addr.point);
      else
        snprintf(ftmp,sizeof(ftmp)-1,"%04x%04x",utmp->addr.net,utmp->addr.node);
#else
      snprintf(ftmp,sizeof(ftmp)-1,"%d.%d.%d.%d", utmp->addr.zone,
	       utmp->addr.net, utmp->addr.node, utmp->addr.point);
#endif /* AMIGA4D */

      /* determine queue directory */
      strcpy(qdir,get_outbound(utmp->addr.zone)); strcat(qdir,"/");
      strcat(qdir,ftmp); strcat(qdir,".gtq/");
      mkdirs(qdir,dirmode(outbound_mode));
/* printf("DEBUG: qdir=%s, utmp=%p\n",qdir,utmp); */

      /* determine bsy file and mk_bsy (make bsy?) variable */
      strcpy(bsy,get_outbound(utmp->addr.zone)); strcat(bsy,"/");
      strcat(bsy,ftmp); strcat(bsy,".bsy");
      if(access_via_stat(bsy,F_OK)==0) /* bsy exist */
        mk_bsy=FALSE;
      else
        mk_bsy=TRUE;

      /* determine flo or hlo file name (if mk_bsy is FALSE, add gtic_q dir) */
      /*
         outbound[/gtic_q]/xxxxyyyy.xlo
         outbound/xxxxyyyy.pnt[/gtic_q]/zzzzzzzz.xlo
      */
      strcpy(xlo,get_outbound(utmp->addr.zone)); strcat(xlo,"/");
#ifndef AMIGA4D
      if(utmp->addr.point!=0)
      {
        snprintf(ftmp,sizeof(ftmp)-1,"%04x%04x.pnt/",
					utmp->addr.net,utmp->addr.node);
				strcat(xlo,ftmp);
      }
#endif /* AMIGA4D */
      if(mk_bsy==FALSE)
      {
				strcat(xlo,"gtic_q/");
				mkdirs(xlo,dirmode(outbound_mode));
      }
#ifndef AMIGA4D
      if(newtic->to.point!=0)
        snprintf(ftmp,sizeof(ftmp)-1,"%08x",newtic->to.point);
      else
        snprintf(ftmp,sizeof(ftmp)-1,"%04x%04x",newtic->to.net,newtic->to.node);
#else
      snprintf(ftmp,sizeof(ftmp)-1,"%d.%d.%d.%d", newtic->to.zone,
	       newtic->to.net, newtic->to.node, newtic->to.point);
#endif /* AMIGA4D */
      if(utmp->flags & USER_HOLD)
        strcat(ftmp,".hlo");
      else
        strcat(ftmp,".flo");
      strcat(xlo,ftmp);

      /* write new tic file */
      /* first find correct tic name */
      /* if user has no_tics flag, skip file */
      if( ! (utmp->flags & USER_NO_TICS) )
      {
        while(1)
        {
          snprintf(ftmp,sizeof(ftmp)-1,"%s/%08lx.tic",qdir,
  	  		(long)time(NULL)+rand());
          if(access_via_stat(ftmp,F_OK)) break;
        }
        fp=fopen(ftmp,"wb");
        if(fp==NULL)
        {
          freetic(newtic);
          tlcur->bad=TRUE;
          mk_badname(tlcur);
          l_printf("BADTIC: tic=%s, area %s, file %s, unable to open newtic %s",
                  tlcur->name,newtic->area,newtic->file,ftmp);
          break;
        }
        if(writeticFILE(newtic,fp))
        {
          freetic(newtic);
          tlcur->bad=TRUE;
	  mk_badname(tlcur);
          l_printf("BADTIC: tic=%s, area %s, file %s, unable to write newtic %s"
	  		,tlcur->name,newtic->area,newtic->file,ftmp);
          break;
        }
        ass(fclose(fp)==0);
      }

      /* 
         try to make hardlinks or copy of file if hardlinks==TRUE
      */
      snprintf(xlo_str,sizeof(xlo_str)-1,"%s/%s",acur->path,newtic->file);
      if(real_make_hardlinks==TRUE)
      {
        /* get file from inbound if current area is "passthru" */
        if(acur->flags & AREA_PASSTHRU)
          snprintf(in_path,sizeof(in_path)-1,"%s/%s",inbound,newtic->realname);
	else
          snprintf(in_path,sizeof(in_path)-1,"%s/%s",acur->path,newtic->file);
	if(first_file==TRUE)
	{
	  strcpy(previous_file,in_path);
	  first_file=FALSE;
	}
        snprintf(out_path,sizeof(out_path)-1,"%s/%s",qdir,newtic->file);
        snprintf(xlo_str,sizeof(xlo_str)-1,"^%s",out_path);
	if(ln(previous_file,out_path))
          if(ln(in_path,out_path))
	  {
	    if(acur->flags & AREA_PASSTHRU)
            {
              freetic(newtic);
              tlcur->bad=TRUE;
              mk_badname(tlcur);
              l_printf("BADTIC: tic=%s, xlo=%s, unable to link file %s to %s,"
	      	       "area %s is passthru",
                  tlcur->name,xlo,xlo_str,in_path,out_path,acur->name);
              break;
             }
	    snprintf(xlo_str,sizeof(xlo_str)-1,"%s/%s",acur->path,newtic->file);
	  }
	strcpy(previous_file,out_path);
      }

      /* write file's path to xlo */
      if(write_to_xlo(xlo,xlo_str[0]=='^'?xlo_str+1:xlo_str,xlo_str[0]=='^',
      		bsy,mk_bsy))
      {
        freetic(newtic);
        tlcur->bad=TRUE;
	mk_badname(tlcur);
        l_printf("BADTIC: tic=%s, xlo=%s, unable to write to xlo [%s]",
                  tlcur->name,xlo,xlo_str);
	break;
      }

      /* write to xlo path_to_tic, ftmp is a tic's name */
      if(write_to_xlo(xlo,ftmp,TRUE,bsy,mk_bsy))
      {
        freetic(newtic);
        tlcur->bad=TRUE;
	mk_badname(tlcur);
        l_printf("BADTIC: tic=%s, xlo=%s, unable to write to xlo [%s]",
                  tlcur->name,xlo,ftmp);
	break;
      }

      l_printf("OK: area=%s, file=%s, node=%s",newtic->area,newtic->file,
        nodetoftn(NULL,&(utmp->addr)));

      freetic(newtic);
    }
    /* if current area is "passthru", delete file from inbound */
    if(acur->flags & AREA_PASSTHRU)
    {
      snprintf(in_path,sizeof(in_path)-1,"%s/%s",inbound,tic->realname);
      if(remove(in_path))
        l_printf("ERROR: unable to delete file %s",in_path);
    }
  }
}

void find_bad_tics(void)
/* find bad tics */
{
  char buff[PATH_MAX],node_buff[FTN_MAX_SIZE]; area_t *atmp;
  struct stat stat_buf; user_t *user_cur;
  int i; tic_t *cur; ticlist_t *tlcur; unsigned long tmp_crc32;
/* printf("DEBUG: ticlist_list_max=%d\n",ticlist_list_max); */
  for(i=0;i<ticlist_list_max;i++)
  {
    tlcur=ilist_find_entry(ticlist_list,i);
    ass(tlcur!=NULL);
		if(tlcur->bad==TRUE)
		{
      mk_badname(tlcur);
			continue;
		}
    cur=tlcur->tic;
    ass(cur!=NULL);
    nodetoftn(node_buff,&(cur->from));
    user_cur=list_find_entry(users,node_buff);

    /* check for file existing */
    /* TODO: fullname support here */
    if(find_file(tlcur->path,buff,cur->file,cur->crc,cur->size))
    {
      /* TODO: old_tic check! */
      l_printf("BADTIC: ticname=%s, file %s not found "
			 "(maybe incorrect crc32 or size, not found by find_file() )",
				tlcur->name,cur->file);
      tlcur->bad=1;
      tlcur->old_tic=OLDTIC_KEEP;
      continue;
    }
    cur->realname=xstrcpy(buff);
    
    /* check for node existing */
    if(!user_cur)
    {
      l_printf("BADTIC: ticname=%s, area %s, file %s - node %s unknown",
      		tlcur->name,cur->area,cur->file,node_buff);
      tlcur->bad=1;
      continue;
    }
    
    /* check for password (TODO: maybe add check from_pwd) */
    if(strcasecmp(user_cur->passwd,cur->pw))
    {
      l_printf("BADTIC: ticname=%s, area %s, node %s - INCORRECT password",
      		tlcur->name,cur->area,node_buff);
      tlcur->bad=1;
      continue;
    }

    snprintf(buff,sizeof(buff)-1,"%s/%s",tlcur->path,cur->realname);
/* printf("DEBUG: file_name=%s\n",buff); */
    if(stat(buff,&stat_buf))
    {
      l_printf("BADTIC: ticname=%s, stat() failed for file %s",
      		tlcur->name,cur->file);
      tlcur->bad=1;
      mk_badname(tlcur);
      continue;
    }

    /* check for file size, if tic_size==-1, don't check */
    if(cur->size!=(unsigned long)(-1) && stat_buf.st_size!=cur->size)
    {
      l_printf("BADTIC: ticname=%s, invalid SIZE in file %s (got %lu, need %lu)",
		tlcur->name,cur->file,stat_buf.st_size,cur->size);
      tlcur->bad=1;
      mk_badname(tlcur);
      continue;
    }

    /* check for crc32 */
		if(crc32_ignore==0)
	    if(cur->crc!=(tmp_crc32=filecrc32(buff)))
	    {
	      l_printf("BADTIC: ticname=%s, invalid CRC32 in file %s"
					"(got %lX, need %lX)",tlcur->name,cur->file,tmp_crc32,cur->crc);
	      tlcur->bad=1;
	      mk_badname(tlcur);
	      continue;
	    }

    /* check for area existing */
    if(!list_find(areas,cur->area))
    {
      l_printf("BADTIC: ticname=%s, area=%s - not-existing area",
      		tlcur->name,cur->area);
      if(user_cur->flags & USER_AUTOCREATE)
      {
        l_printf("BADTIC: area %s passed to autocreate",cur->area);
	list_add(newareas_descs,cur->area,cur->areadesc?cur->areadesc:"");
        list_add(newareas_users,cur->area,user_cur);
      }
      tlcur->bad=1;
      continue;
    }
    
    /* check for node is exist and not-readonly for this area */
    /* find from area_t->users by key=ftn_node_string (without ! in 1st pos) */
    atmp=list_find_entry(areas,cur->area);
    ass(atmp!=NULL);


		/* assume local address (aka or whoami) connected without checking */

		if( ! ( nodecmp(&(cur->from),&whoami)==0 ||
				(atmp->aka && nodecmp(&(cur->from),atmp->aka))==0 )
			)
		{

			user_cur=list_find_entry(atmp->users,node_buff);

			/* TODO: maybe make report for readonly node here */
	    if(!user_cur)
	    {
	      l_printf(
				 "BADTIC: tic=%s, file %s, node %s r/o or not exist for area %s",
	      		tlcur->name,cur->file,node_buff,cur->area);
	      tlcur->bad=1;
	      continue;
	    }
	
	    /* check for group validation */
	    if(!validate_group(atmp,user_cur))
	    {
	      l_printf(
				 "BADTIC: tic=%s, file %s, node %s, area %s don't have group %s.",
	      		tlcur->name,cur->file,node_buff,cur->area,atmp->group);
	      tlcur->bad=1;
	      continue;
	    }

		}

/*	printf("*** %p,%p\n",tlcur->name,cur->realname); */
    l_printf("TIC_OK: name=%s, file=%s",tlcur->name,cur->realname);
  }
}

void move_bad_tics(void)
/* move bad tics to bad_dir and delete if old_tic=OLDTIC_REMOVE */
{
  char buff[PATH_MAX],buff2[PATH_MAX];
  int i; tic_t *cur; ticlist_t *tlcur;
  for(i=0;i<ticlist_list_max;i++)
  {
    tlcur=ilist_find_entry(ticlist_list,i);
    ass(tlcur!=NULL);
    cur=tlcur->tic;
    ass(cur!=NULL);
    if(tlcur->bad==FALSE) continue;
    if(tlcur->old_tic==OLDTIC_REMOVE)
    {
      snprintf(buff,sizeof(buff)-1,"%s/%s",tlcur->path,tlcur->name);
      if(remove(buff))
        l_printf("unable to delete old tic %s",buff);
      continue;
    }
    if(tlcur->type==TICLIST_BADDIR && tlcur->badname==NULL) continue;
    if(tlcur->old_tic==OLDTIC_KEEP) continue;
    mkdirs(bad_dir,bad_dir_mode);
    /* move tic file */
    snprintf(buff,sizeof(buff)-1,"%s/%s",tlcur->path,tlcur->name);
    snprintf(buff2,sizeof(buff2)-1,"%s/%s",
      bad_dir,tlcur->badname?tlcur->badname:tlcur->name);
    if(mv(buff,buff2))
    {
      l_printf("unable to move %s to %s",buff,buff2);
    }
    /* move file (is tic_t->realname != NULL) */
/* fprintf(stderr,"DEBUG: %s %s\n",cur->file,cur->realname); */
    if(cur->realname)
    {
      snprintf(buff,sizeof(buff)-1,"%s/%s",tlcur->path,cur->realname);
      snprintf(buff2,sizeof(buff2)-1,"%s/%s",bad_dir,cur->realname);
      if(mv(buff,buff2))
      {
        l_printf("unable to move %s to %s",buff,buff2);
      }
    }
  }
}

void create_new_areas(void)
{
  int i,child_pid,w,j; list_t *tmp_d,*tmp_u; char *a1,*a2;
  char node_buff[FTN_MAX_SIZE]; user_t *tmp_usert;
  char buff[BUFSIZE],node_buff2[FTN_MAX_SIZE];
  for(i=0;i<list_head_max(newareas_descs/*same as newareas_users*/);i++)
  {
    tmp_d=list_get(newareas_descs,i); tmp_u=list_get(newareas_users,i);
    ass(tmp_d!=NULL && tmp_u!=NULL);
    if(list_entry(tmp_d))
      if(*((char*)list_entry(tmp_d))!=0)
      {
/* printf("!%s!\n",(char*)list_entry(tmp_d)); */
        setenv("newarea_desc",list_entry(tmp_d),1);
      }
    a1=list_key(tmp_d); a2=node_buff;
    tmp_usert=list_entry(tmp_u);
    nodetoftn(node_buff,&(tmp_usert->addr));

    
    /* Dangerous: maybe need dynamically allocated buffer for
       $autoconnected_users */
    /* auto_ro имеет больший приоритет, чем auto_rw */
    buff[0]=0;
    for(j=0;j<list_head_max(users);j++)
    {
      user_t *tmpu; list_t *tmpl;
      tmpl=list_get(users,j);
      ass(tmpl!=NULL);
      tmpu=list_entry(tmpl);
      ass(tmpu!=NULL);
      if((tmpu->flags & USER_AUTO_RW) || (tmpu->flags & USER_AUTO_RO))
      {
        strcpy(node_buff2," !");
	nodetoftn(&node_buff2[(tmpu->flags & USER_AUTO_RO)?2:1],&(tmpu->addr));
	strcat(buff,node_buff2);
      }
    }
/* printf("DEBUG: %s\n",buff); */
    if(buff[0]!=0) setenv("autoconnected_users",buff,1);
    
    child_pid=fork();
    if(child_pid==0)
    {
      /* exec external autocreator */
			swap_uid_gid();
      execl(newarea_exec,newarea_exec,a1,a2,NULL);
      e_printf("exec of newarea_exec \"%s\" failed",newarea_exec);
      exit(0);
    }
    else if(child_pid==-1)
    {
      e_printf("fork() for newarea_exec failed");
    }
    else
    {
      while(1)
      {
        w=wait(NULL);
	if(w==0 || w==-1) break;
      }
    }
  }
} 
void toss_tics(void)
/*
  move files to fileechos, mark tics with unmovable files as bad
*/
{
  char oldname[PATH_MAX],newname[PATH_MAX],buff[BUFSIZE],*p;
  FILE *fp,*fp2,*announce_f=NULL,*private_a_f;
  int i,j; tic_t *cur; ticlist_t *tlcur; area_t *curarea;
  for(i=0;i<ticlist_list_max;i++)
  {
    tlcur=ilist_find_entry(ticlist_list,i);
    ass(tlcur!=NULL);
    cur=tlcur->tic;
    ass(cur!=NULL);
    if(tlcur->bad==TRUE) continue;
    curarea=list_find_entry(areas,cur->area);
    ass(curarea!=NULL);

    /* skip file if area is "passthru" */
    if(curarea->flags & AREA_PASSTHRU) continue;

/*
printf("DEBUG: cur=%p\n",cur);
printf("DBG: %p\n",cur->replaces);
printf("DBG: %s\n",cur->replaces?cur->replaces:"(NULL)");
*/
    /* process "Replaces" keyword */
    if(cur->replaces)
    {
      char *tmp1,*tmp2;

			if( ! (curarea->flags & AREA_NO_REPLACES) )
			{
	      if( check_replaces(cur->file,cur->replaces) == FALSE )
	      {
	        /* attempt to hack with "Replaces" */
	        l_printf("BADTIC: %s: attempt to hack with \"Replaces\" (%s,%s)",
					  tlcur->name,cur->file,cur->replaces);
	        tlcur->bad=1;
	        mk_badname(tlcur);
	        continue;
	      }
			}
			else
			{
				/* insecure replaces check */
				if(strchr("[]*?",cur->replaces[0]))
	      {
	        /* attempt to hack with "Replaces" (2, small security) */
	        l_printf("BADTIC: %s: attempt to hack with \"Replaces\" (2) (%s,%s)",
					  tlcur->name,cur->file,cur->replaces);
	        tlcur->bad=1;
	        mk_badname(tlcur);
	        continue;
	      }
			}
      
      ass(curarea->path!=NULL);
      rm_by_mask(curarea->path,cur->replaces);
      
      tmp1=xstrcpy(cur->replaces);
      tmp1=xstrcat(tmp1,".desc");

      if(strchr(desc_dir,'/'))
      {
        tmp2=xstrcpy(desc_dir);
      }
      else
      {
        tmp2=xstrcpy(curarea->path);
				tmp2=xstrcat(tmp2,"/");
				tmp2=xstrcat(tmp2,desc_dir);
      }

/* printf("DEBUG: tmp2=%s, tmp1=%s\n",tmp2,tmp1); */
      rm_by_mask(tmp2,tmp1);

      xfree(tmp1);
      xfree(tmp2);

    }

    mkdirs(curarea->path,dirmode(curarea->mode==-1?file_mode:curarea->mode));
    snprintf(oldname,sizeof(oldname)-1,"%s/%s",tlcur->path,cur->realname);
    snprintf(newname,sizeof(newname)-1,"%s/%s",curarea->path,cur->file);

    if(mv(oldname,newname))
    {
      l_printf("BADTIC: unable to move file %s from %s/ to %s/",
        cur->realname,tlcur->path,curarea->path);
      tlcur->bad=1;
      mk_badname(tlcur);
      continue;
    }
    chmod(newname,curarea->mode==-1?file_mode:curarea->mode);

    /* write desc_file and "newfile" file only cur->desc and cur->ldesc */
    /* skip announce_exec call if area is AREA_NOANNOUNCE */
    if(cur->desc==NULL && cur->ldesc==NULL) continue;
    if(strchr(desc_dir,'/'))
    {
      strcpy(newname,desc_dir);
      mkdirs(newname,dirmode(curarea->mode==-1?file_mode:curarea->mode));

      snprintf(newname,sizeof(newname)-1,"%s/%s.desc",desc_dir,cur->file);
    }
    else
    {
      snprintf(newname,sizeof(newname)-1,"%s/%s",curarea->path,desc_dir);
      mkdirs(newname,dirmode(curarea->mode==-1?file_mode:curarea->mode));

      snprintf(newname,sizeof(newname)-1,"%s/%s/%s.desc",
      	curarea->path,desc_dir,cur->file);
    }
    private_a_f=NULL;
    /* skip .desc if curarea has "no_desc" option */
    if( ! (curarea->flags & AREA_NO_DESC) )
      fp=fopen(newname,"w");
    fp2=fopen(curarea->newfile?curarea->newfile:default_newfile,"a");
    ass(announce_exec!=NULL);
    if( ! (curarea->flags & AREA_NOANNOUNCE) )
      if(announce_exec[0]!=0 && announce_f==NULL)
      {
        char buff[PATH_MAX];
        /* TODO: maybe some security check here */
	if(curarea->flags & AREA_PRIVATE_ANNOUNCE)
	  if(curarea->group)
	  {
	    snprintf(buff,sizeof(buff)-1,"%s.%s",announce_exec,curarea->group);
      swap_uid_gid();
	    private_a_f=popen(buff,"w");
      swap_uid_gid();
	  }
	      swap_uid_gid();
        announce_f=popen(announce_exec,"w");
  	    swap_uid_gid();
        if(announce_f) fprintf(announce_f,"\n");
      }

    if(fp2)
		{
      fprintf(fp2,"AREA: %-15s FILE: %s\n",curarea->name,cur->file);
			if(pub_path && pub_url &&
				strlen(curarea->path)>=strlen(pub_path) &&
				strncmp(curarea->path,pub_path,strlen(pub_path))==0) 
			{
				fprintf(fp2,"URL: %s%s/%s\n",pub_url,
				 curarea->path+strlen(pub_path),cur->file);
			}
		}
      
    if(announce_f)
		{
      fprintf(announce_f,">AREA: %-15s FILE: %s\n",curarea->name,cur->file);
			if(pub_path && pub_url &&
				strlen(curarea->path)>=strlen(pub_path) &&
				strncmp(curarea->path,pub_path,strlen(pub_path))==0) 
			{
				fprintf(announce_f,"URL: %s%s/%s\n",pub_url,
				 curarea->path+strlen(pub_path),cur->file);
			}
		}
      
    for(j=0;j<cur->max_desc;j++)
    {
      p=ilist_find_entry(cur->desc,j);
      ass(p!=NULL);
      strcpy(buff,p);
      a2k(buff,strlen(buff));
      if(fp)
        fprintf(fp,"%s\n",buff);
      if(fp2)
        fprintf(fp2,"%s\n",buff);
      if(announce_f)
        fprintf(announce_f,"%s\n",buff);
    }
    for(j=0;j<cur->max_ldesc;j++)
    {
      p=ilist_find_entry(cur->ldesc,j);
      ass(p!=NULL);
      strcpy(buff,p);
      a2k(buff,strlen(buff));
      if(fp)
        fprintf(fp,"%s\n",buff);
      if(fp2)
        fprintf(fp2,"%s\n",buff);
      if(announce_f)
        fprintf(announce_f,"%s\n",buff);
    }

    if(fp2) fprintf(fp2,"\n");
    if(announce_f) fprintf(announce_f,"\n");
    
    if(fp) fclose(fp);
    if(fp2) fclose(fp2);
    if(private_a_f) fclose(private_a_f);
  }
  if(announce_f) pclose(announce_f);
}

void remove_good_tics(void)
/* remove all of good tics after tossing and creating outbound */
{
  char name[PATH_MAX]; int i; ticlist_t *tlcur;
  for(i=0;i<ticlist_list_max;i++)
  {
    tlcur=ilist_find_entry(ticlist_list,i);
    ass(tlcur!=NULL);
    if(tlcur->bad==TRUE) continue; /* remove only good tics */
    snprintf(name,sizeof(name)-1,"%s/%s",tlcur->path,tlcur->name);
    if(remove(name))
    {
      l_printf("BADTIC: ticname=%s, unable to delete good tic (file is ok)",
      		tlcur->name);
      tlcur->bad=1;
      mk_badname(tlcur);
      continue;
    }
  }
}

void toss(void)
{
  newareas_descs=list_init();
  newareas_users=list_init();
  process_gtic_q();
  find_bad_tics();
  toss_tics();
  create_outbound();
  remove_good_tics();
  move_bad_tics();
  if(newarea_exec!=NULL)
    if(newarea_exec[0]!=0)
      create_new_areas();
  list_free(newareas_descs);
  list_free(newareas_users);
}


syntax highlighted by Code2HTML, v. 0.9.1