/*
  tic files support
*/

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

#include "config.h"

ilist_t *ticlist_list;
int ticlist_list_max;

void readtic(ticlist_t *tlist)
/* read tic file from ony ticlist_t */
{
  char buff[BUFSIZE>PATH_MAX?BUFSIZE:PATH_MAX]; FILE *fp; tic_t *cur;
  int linecnt; char *v,*k; node_t tmp_node; int i,j;
  ass(tlist!=NULL);
  ass(tlist->tic==NULL);
  ass(tlist->name!=NULL);
  ass(tlist->path!=NULL);
  strcpy(buff,tlist->path); strcat(buff,"/"); strcat(buff,tlist->name);
  fp=fopen(buff,"rb");
  if(fp==NULL)
  {
    e_printf("readtic: unable to read ticfile \"%s\" - ignored",
    		buff);
    /* TODO: mark tic as bad here */
    return;
  }
  cur=xmalloc(sizeof(tic_t));

  /* tic_t initialization */
  cur->area=NULL;
  cur->areadesc=NULL;
	cur->file=NULL;
	cur->realname=NULL;
	cur->fullname=NULL;
	cur->magic=NULL;
	cur->replaces=NULL;
  cur->ldesc=NULL;
  cur->desc=NULL;
  cur->max_desc=0;
  cur->max_ldesc=0;
  cur->size=-1;
  cur->date=-1;
  cur->release=-1;
  cur->author=NULL;
  cur->source=NULL;
  cur->app=NULL;
  cur->max_app=0;
  (cur->origin).zone=0;
  (cur->from).zone=0;
  cur->from_pwd=NULL;
  (cur->to).zone=0;
  cur->created=NULL;
  cur->via=NULL;
  cur->max_via=0;
  cur->path=NULL;
  cur->max_path=0;
  cur->seenby=NULL;
  cur->pw=xstrcpy("*"); /* see Notes2 */

  linecnt=0;

  while(fgets(buff,sizeof(buff)-1,fp))
  {
    delcrlf(buff);
    linecnt++;
    if(buff[0]==0) continue;
    v=strschr(buff,DELIM);
/* printf("DEBUG: %s=%s\n",buff,v?v:"?null?");*/
    if(v==NULL) continue; /* blank keywords should be dropped (by FSC-0087) */

		/* this string must follows v==NULL check */
		strschr2(buff,DELIM)[0]=0;

    if(!strcasecmp(buff,"area"))
    {
      cur->area=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"areadesc"))
    {
      cur->areadesc=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"file"))
    {
      /* if all chars of v isupper(), change filename to lower case */
      j=strlen(v);
      for(i=0;i<j;i++)
        if(isalpha(v[i]) && islower(v[i]))
          break;
      if(i==j)
        for(i=0;i<j;i++)
      v[i]=tolower(v[i]);
			/* Security check: remove / * ? [ ] from filename */
			strremove(v,"/");
			strremove(v,"*");
			strremove(v,"?");
			strremove(v,"[");
			strremove(v,"]");
      cur->file=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"fullname"))
    {
      cur->fullname=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"magic"))
    {
      cur->magic=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"replaces"))
    {
      cur->replaces=xstrcpy(v);
/*printf("FUCK: %p,%s\n",cur->replaces,cur->replaces);*/
/*printf("DEBUG2: cur=%p\n",cur);*/
    }
    else if(!strcasecmp(buff,"author"))
    {
      cur->author=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"created"))
    {
      cur->created=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"pw"))
    {
      if(cur->pw) xfree(cur->pw); /* see Notes2 */
      cur->pw=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"crc"))
    {
      cur->crc=strtoul(v,NULL,16);
    }
    else if(!strcasecmp(buff,"size"))
    {
      cur->size=strtoul(v,NULL,10);
    }
    else if(!strcasecmp(buff,"date"))
    {
      cur->date=strtoul(v,NULL,10);
    }
    else if(!strcasecmp(buff,"release"))
    {
      cur->release=strtoul(v,NULL,10);
    }
    else if(!strcasecmp(buff,"source"))
    {
      cur->source=xstrcpy(v);
    }
    else if(!strcasecmp(buff,"origin"))
    {
			if(ftntonode(&(cur->origin),v,NULL))
				e_printf("bad address \"%s\"",v);
    }
    else if(!strcasecmp(buff,"to"))
    {
			if(ftntonode(&(cur->to),v,NULL))
				e_printf("bad address \"%s\"",v);
    }
    else if(!strcasecmp(buff,"from"))
    {
      k=strchr(buff,' '); if(k) k[0]=0;
			if(ftntonode(&(cur->from),v,NULL))
				e_printf("bad address \"%s\"",v);
      if(k) cur->from_pwd=xstrcpy(k+1);
    }
    else if(!strcasecmp(buff,"desc"))
    {
      k=xstrcpy(v);
      if(cur->max_desc==0)
      {
        cur->desc=ilist_init();
      }
      ilist_add(cur->desc,cur->max_desc,k);
      cur->max_desc++;
    }
    else if(!strcasecmp(buff,"ldesc"))
    {
      k=xstrcpy(v);
      if(cur->max_ldesc==0)
      {
        cur->ldesc=ilist_init();
      }
      ilist_add(cur->ldesc,cur->max_ldesc,k);
      cur->max_ldesc++;
    }
    else if(!strcasecmp(buff,"path"))
    {
      k=xstrcpy(v);
      if(cur->max_path==0)
      {
        cur->path=ilist_init();
      }
      ilist_add(cur->path,cur->max_path,k);
      cur->max_path++;
    }
    else if(!strcasecmp(buff,"via"))
    {
      k=xstrcpy(v);
      if(cur->max_via==0)
      {
        cur->via=ilist_init();
      }
      ilist_add(cur->via,cur->max_via,k);
      cur->max_via++;
    }
    else if(!strcasecmp(buff,"seenby"))
    {
/*      k=xstrcpy(v);*/
      if(cur->seenby==NULL)
        cur->seenby=list_init();
			if (ftntonode(&tmp_node,v,NULL))
				e_printf("bad address \"%s\"", v);
      nodetoftn(buff,&tmp_node);
      list_add(cur->seenby,buff,"");
    }
    else if(!strcasecmp(buff,"Destination")) continue; /* skip for ALLFIX+ */
    else if(!strcasecmp(buff,"pgp")) continue; /* skip by FSC-0087 */
    else if(!strcasecmp(buff,"ReceiptRequest")) continue; /* skip by FSC-0087 */
    else
    {
      l_printf("readtic: %s(%d) - %s[%s] - keyword unknown - ignored",
      	tlist->name,linecnt,buff,v);
      continue;
    }
  }
	
  if(cur->area==NULL ||
     cur->file==NULL ||
     cur->crc==0     || /* TODO: rewrite unknown-crc32 check */
     (cur->origin).zone==0 ||
     (cur->from).zone==0   ||
     cur->created==NULL    ||
     cur->path==NULL       ||
     cur->seenby==NULL     ||
     cur->pw==NULL
    )
  {
    tlist->bad=TRUE;
    l_printf("BADTIC: ticname=%s, not full tic-header (see FSC-0087)",
      tlist->name);
  }

  tlist->tic=cur;

  fclose(fp);
}

void read_tics_from_list(void)
/* read all tic files from ticlist_list */
{
  int i; ilist_t *tmp;
  ass(ticlist_list!=NULL);
  for(i=0;i<ticlist_list_max;i++)
  {
    tmp=ilist_find(ticlist_list,i);
    ass(tmp!=NULL);
    ass(list_entry(tmp)!=NULL);
/*printf("DEBUG: %d\r",i);*/
/*fflush(stdout);*/
    readtic((ticlist_t *)list_entry(tmp));
  }
}

#define TICPATTERN "*.tic"

static void _readticlist_readdir(char *dirname,int ticlist_type)
{
  DIR *dir; struct dirent *dirp; struct stat stat_buf;
  char buff[PATH_MAX]; ticlist_t *cur;
  dir=opendir(dirname);
  if(dir==NULL && ticlist_type==TICLIST_BADDIR) return;
  if(dir==NULL) e_printf("unable to read inbound dir %s",dirname);
  ass(dir!=NULL);
  while(1)
  {
    dirp=readdir(dir);
    if(!dirp) break;
    if(wildmat_icase(dirp->d_name,TICPATTERN))
    {
      strcpy(buff,dirname); strcat(buff,"/"); strcat(buff,dirp->d_name);
      ass(stat(buff,&stat_buf)==0);
      if(!(stat_buf.st_mode & S_IFREG)) continue;
      /* ticlist_t initialization */
      cur=(ticlist_t *)xmalloc(sizeof(ticlist_t));
      cur->bad=FALSE;
      cur->type=ticlist_type;
      cur->path=dirname;
      cur->name=xstrcpy(dirp->d_name);
      cur->badname=NULL;
      cur->old_tic=OLDTIC_OK;
      cur->ctime=stat_buf.st_ctime;
      cur->tic=NULL;
/*printf("DEBUG: _readticlist_readdir - type=%d, path=%s, name=%s, ctime=%d\n",
cur->type,cur->path,cur->name,(int)cur->ctime);
*/
      ilist_add(ticlist_list,ticlist_list_max++,cur);
    }
  }
  closedir(dir);
}

void freeticlist(void)
/* free all ticlist_t from ticlist_list */
{
  int i; ticlist_t *tl_tmp;
  if(!ticlist_list)
  {
    e_printf("warning: trying to freeticlist zero ticlist_t - ignored");
    return;
  }
  for(i=0;i<ticlist_list_max;i++)
  {
    tl_tmp=ilist_find_entry(ticlist_list,i);
    ass(tl_tmp!=NULL);
    if(tl_tmp->badname) xfree(tl_tmp->badname);
    if(tl_tmp->name) xfree(tl_tmp->name);
		if(tl_tmp->bad==FALSE)	/* Free tic in memory ONLY if this is GOOD tic */
	    if(tl_tmp->tic)
				freetic(tl_tmp->tic);
    xfree(tl_tmp);
  }
  ilist_free(ticlist_list);
}

void readticlist(void)
/* read tics from inbound and possibly from bad_dir */
{
  ticlist_list_max=0;
  ticlist_list=ilist_init();
  _readticlist_readdir(inbound,TICLIST_INBOUND);
  if(toss_badtic)
    _readticlist_readdir(bad_dir,TICLIST_BADDIR);
  if(ticlist_list_max==0)
  {
    l_printf("EXIT: no tics found");
    exit(0);
  }
}

static void _free_ilist(ilist_t *lst,int max,char *info,tic_t *tic)
{
  int i; char *ctmp;
/*printf("DEBUG: &lst=%p\n",lst);*/
  if(lst)
  {
    for(i=0;i<max;i++)
    {
      ctmp=ilist_find_entry(lst,i);
      if(ctmp==NULL)
      {
        e_printf("internal freeing-%s error [ilist_find==NULL or"
                       "ilist_entry(tmp)==NULL] (tic->file=%s,key=%d)",
          info,tic->file?tic->file:"?null pointer?",i);
      }
/*printf("DEBUG: %s\n",ctmp);      */
      xfree(ctmp);
    }
    ilist_free(lst);
  }
}

void freetic(tic_t *tic)
/* frees one tic_t */
{
  if(!tic)
  {
    e_printf("warning: trying to freetic zero tic_t - ignored");
    return;
  }

  _free_ilist(tic->desc,   tic->max_desc,   "desc",   tic);
  _free_ilist(tic->ldesc,  tic->max_ldesc,  "ldesc",  tic);
  _free_ilist(tic->app,    tic->max_app,    "app",    tic);
  _free_ilist(tic->via,    tic->max_via,    "via",    tic);
  _free_ilist(tic->path,   tic->max_path,   "path",   tic);

  list_free(tic->seenby);

  if(tic->area)		xfree(tic->area);
  if(tic->areadesc)	xfree(tic->areadesc);
  if(tic->file)		xfree(tic->file);
  if(tic->realname)	xfree(tic->realname);
  if(tic->fullname)	xfree(tic->fullname);
  if(tic->magic)	xfree(tic->magic);
  if(tic->replaces)	xfree(tic->replaces);
  if(tic->author)	xfree(tic->author);
  if(tic->source)	xfree(tic->source);
  if(tic->from_pwd)	xfree(tic->from_pwd);
  if(tic->created)	xfree(tic->created);
  if(tic->pw)		xfree(tic->pw);

  xfree(tic);
}

tic_t *copytic(tic_t *tic)
/*
copy all members of tic_t to new tic_t
allocate memory for any of pointer of original tic to new tic and copy pointer
content
*/
{
  tic_t *tmp; int i;
  list_t *ltmp;
  char *ctmp;

  tmp=xmalloc(sizeof(tic_t));

  tmp->desc=NULL;
  tmp->ldesc=NULL;
  tmp->app=NULL;
  tmp->via=NULL;
  tmp->path=NULL;
  tmp->seenby=NULL;

  tmp->crc=tic->crc;
  tmp->size=tic->size;
  tmp->date=tic->date;
  tmp->release=tic->release;

  tmp->max_desc=tic->max_desc;
  tmp->max_ldesc=tic->max_ldesc;
  tmp->max_app=tic->max_app;
  tmp->max_via=tic->max_via;
  tmp->max_path=tic->max_path;

  nodecpy(&(tmp->origin),&(tic->origin));
  nodecpy(&(tmp->from),&(tic->from));
  nodecpy(&(tmp->to),&(tic->to));

  /* xstrcpy returns NULL if argument==NULL */
  tmp->area=xstrcpy(tic->area);
  tmp->source=xstrcpy(tic->source);
  tmp->areadesc=xstrcpy(tic->areadesc);
  tmp->file=xstrcpy(tic->file);
  tmp->realname=xstrcpy(tic->realname);
  tmp->fullname=xstrcpy(tic->fullname);
  tmp->magic=xstrcpy(tic->magic);
  tmp->replaces=xstrcpy(tic->replaces);
  tmp->author=xstrcpy(tic->author);
  tmp->from_pwd=xstrcpy(tic->from_pwd);
  tmp->created=xstrcpy(tic->created);
  tmp->pw=xstrcpy(tic->pw);

  if(tic->max_desc)
  {
    tmp->desc=ilist_init();
    for(i=0;i<tic->max_desc;i++)
    {
      ctmp=ilist_find_entry(tic->desc,i);
      ass(ctmp!=NULL);
      ilist_add(tmp->desc,i,xstrcpy(ctmp));
    }
  }
  if(tic->max_ldesc)
  {
    tmp->ldesc=ilist_init();
    for(i=0;i<tic->max_ldesc;i++)
    {
      ctmp=ilist_find_entry(tic->ldesc,i);
      ass(ctmp!=NULL);
      ilist_add(tmp->ldesc,i,xstrcpy(ctmp));
    }
  }
  if(tic->max_app)
  {
    tmp->app=ilist_init();
    for(i=0;i<tic->max_app;i++)
    {
      ctmp=ilist_find_entry(tic->app,i);
      ass(ctmp!=NULL);
      ilist_add(tmp->app,i,xstrcpy(ctmp));
    }
  }
  if(tic->max_via)
  {
    tmp->via=ilist_init();
    for(i=0;i<tic->max_via;i++)
    {
      ctmp=ilist_find_entry(tic->via,i);
      ass(ctmp!=NULL);
      ilist_add(tmp->via,i,xstrcpy(ctmp));
    }
  }
  if(tic->max_path)
  {
    tmp->path=ilist_init();
    for(i=0;i<tic->max_path;i++)
    {
      ctmp=ilist_find_entry(tic->path,i);
      ass(ctmp!=NULL);
      ilist_add(tmp->path,i,xstrcpy(ctmp));
    }
  }
  if(tic->seenby) /* maybe check list_head_max of seenby? */
  {
    tmp->seenby=list_init();
    for(i=0;i<list_head_max(tic->seenby);i++)
    {
      ltmp=list_get(tic->seenby,i);
      ass(ltmp!=NULL);
      ass(list_key(ltmp)!=NULL);
      list_add(tmp->seenby,list_key(ltmp),"");
    }
  }
  return tmp;
}

int writeticFILE(tic_t *tic,FILE *fp)
{
  char buff[BUFSIZE],ftmp[FTN_MAX_SIZE]; int i;
  list_t *ltmp; char *p;

  if(fp==NULL) return 1;

  if(tic->area)
  {
    snprintf(buff,sizeof(buff)-1,"Area %s\r\n",tic->area);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->areadesc)
  {
    snprintf(buff,sizeof(buff)-1,"Areadesc %s\r\n",tic->areadesc);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->file)
  {
    snprintf(buff,sizeof(buff)-1,"File %s\r\n",tic->file);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->fullname)
  {
    snprintf(buff,sizeof(buff)-1,"Fullname %s\r\n",tic->fullname);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  snprintf(buff,sizeof(buff)-1,"Crc %lX\r\n",tic->crc);
	str_truncate(256,buff);
  if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;

  if(tic->magic)
  {
    snprintf(buff,sizeof(buff)-1,"Magic %s\r\n",tic->magic);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->replaces)
  {
    snprintf(buff,sizeof(buff)-1,"Replaces %s\r\n",tic->replaces);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->max_desc)
    for(i=0;i<tic->max_desc;i++)
    {
      p=ilist_find_entry(tic->desc,i);
      ass(p!=NULL);
      snprintf(buff,sizeof(buff)-1,"Desc %s\r\n",p);
			str_truncate(80,buff);
      if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
    }

  if(tic->max_ldesc)
    for(i=0;i<tic->max_ldesc;i++)
    {
      p=ilist_find_entry(tic->ldesc,i);
      ass(p!=NULL);
      snprintf(buff,sizeof(buff)-1,"Ldesc %s\r\n",p);
			str_truncate(80,buff);
      if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
    }
  
  if(tic->size!=(unsigned long)(-1))
  {
    snprintf(buff,sizeof(buff)-1,"Size %lu\r\n",tic->size);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->date!=(time_t)(-1))
  {
    snprintf(buff,sizeof(buff)-1,"Date %lu\r\n",tic->date);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }
  
  if(tic->release!=(time_t)(-1))
  {
    snprintf(buff,sizeof(buff)-1,"Release %lu\r\n",tic->release);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->author)
  {
    snprintf(buff,sizeof(buff)-1,"Author %s\r\n",tic->author);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->source)
  {
    snprintf(buff,sizeof(buff)-1,"Source %s\r\n",tic->source);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }
    
  if(tic->max_app)
    for(i=0;i<tic->max_app;i++)
    {
      p=ilist_find_entry(tic->app,i);
      ass(p!=NULL);
      snprintf(buff,sizeof(buff)-1,"App %s\r\n",p);
			str_truncate(256,buff);
      if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
    }

  if((tic->origin).zone!=0)
  {
    nodetoftn(ftmp,&(tic->origin));
    snprintf(buff,sizeof(buff)-1,"Origin %s\r\n",ftmp);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }
  
  if((tic->from).zone!=0)
  {
    nodetoftn(ftmp,&(tic->from));
    snprintf(buff,sizeof(buff)-1,"From %s%s%s\r\n",
    	ftmp,tic->from_pwd?" ":"",tic->from_pwd?tic->from_pwd:"");
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }
  
  if((tic->to).zone!=0)
  {
    nodetoftn(ftmp,&(tic->to));
    snprintf(buff,sizeof(buff)-1,"To %s\r\n",ftmp);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->created)
  {
    snprintf(buff,sizeof(buff)-1,"Created %s\r\n",tic->created);
		str_truncate(256,buff);
    if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
  }

  if(tic->max_via)
    for(i=0;i<tic->max_via;i++)
    {
      p=ilist_find_entry(tic->via,i);
      ass(p!=NULL);
			str_truncate(256,p);
      if(fwrite(p,1,strlen(p),fp)!=strlen(p)) return 1;
    }
    
  if(tic->max_path)
	{
		list_t *dupes;
		dupes=list_init();
    for(i=0;i<tic->max_path;i++)
    {
			char smallbuff[256],*p_fuck;
      p=ilist_find_entry(tic->path,i);
      ass(p!=NULL);
			strncpy(smallbuff,p,sizeof(smallbuff)-1);
			if((p_fuck=strchr(smallbuff,' ')))
			{
				*(p_fuck+1)=0; /* FIXME: buffer overflow check */
				if(list_find(dupes,smallbuff)) continue;
				list_add(dupes,smallbuff,"");
			}
			snprintf(buff,sizeof(buff)-1,"Path %s\r\n",p);
			str_truncate(256,p);
			if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff))
			{
				list_free(dupes);
				return 1;
			}
    }
		list_free(dupes);
	}

  if(tic->seenby)
    for(i=0;i<list_head_max(tic->seenby);i++)
    {
      ltmp=list_get(tic->seenby,i);
      ass(ltmp!=NULL);
      p=list_key(ltmp);
      ass(p!=NULL);
      snprintf(buff,sizeof(buff)-1,"SeenBy %s\r\n",p);
			str_truncate(256,buff);
      if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
    }

  if(tic->pw)
  {
    if(strcmp(tic->pw,"*")) /* see Notes2 */
    {
      snprintf(buff,sizeof(buff)-1,"Pw %s\r\n",tic->pw);
			str_truncate(256,buff);
      if(fwrite(buff,1,strlen(buff),fp)!=strlen(buff)) return 1;
    }
  }

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1