//
// File: scolsign.cpp
//
// Modification history:
//
//$ FA(17/07/2001): Promoted to C++ file
//$ FA(17/07/2001): Use lexer utility function a2i() as a replacement of Mgetnum()
//$ FA(02/08/2001): mkdir() function for GNU compiler is defined in sys/stat.h
//

#include "scolPrerequisites.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "compiler/lexer.h"
extern "C"
{
#include "common/vscol.h"
#include "scolMMemory.h"
#include "vm/mbytec.h"
#include "scol.h"
#include "scolsign.h"
#include "scolpack.h"
#include "scolSystem.h"
}

#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
#  include <direct.h>
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
#  include <sys/stat.h>
#  include <fcntl.h>
#  include <unistd.h>
#endif

#define NBPARTMAX 32
char namepart[NBPARTMAX][128];
char parampart[NBPARTMAX][128];
int  typepart[NBPARTMAX];
int  ipart=0;

packdir Firstpack=NULL;
packdir Cachepack=NULL;
char cookies[64];

int SPregistPart(char* name, char* param, int type)
{
	if (ipart >= NBPARTMAX)
		return 0;
	
	strcpy(namepart[ipart], name);
	strcpy(parampart[ipart], param);
	typepart[ipart++] = type;	
	return 0;
}

int SPunRegistPart()
{
  packdir p, q;
	if ((ipart >= NBPARTMAX) || (ipart < 1))
		return 0; 

  p = Firstpack;
  while(p->next)
  {
	q = p;
 	p = p->next;
  }
  free(p);
  q->next = NULL;

	strcpy(namepart[ipart], "");
	strcpy(parampart[ipart], "");
	typepart[ipart--] = NULL;
	return 0;
}


packdir SPallocpackdir()
// Subsumed by Partition::Partition(...)
{
  return (packdir)malloc(SIZEPACKDIR);
}


/* desalloue les packages */
int SPdesallocpackdir()
{
  packdir p, q;

  p = Firstpack;
  while(p)
  {
	  q = p->next;
	  free(p);
	  p = q;
  }
  Cachepack = Firstpack = NULL;
  ipart = 0;
  return 0;
}


extern "C"
{
  extern char execpath[MAX_PATH];  // defined in Myloop.c
}

//$BLG - v5.2.06: Modif
/*
packdir SPcreatedir(char *name,char *param, packdir last)
// Use Partition::Partition(...) instead.
{
  int  c;
  packdir p;

  if ((p=SPallocpackdir())==NULL) return NULL;
  if ((!strncmp(name,"./",2))||(!strncmp(name,".\\",2)))
  {
	  sprintf(p->path,"%s%s",execpath,name+1);
  }
  else strcpy(p->path,name);
  c=p->path[strlen(p->path)-1];
  if ((c!='/')&&(c!='\\')) strcat(p->path,"/");
  if (param[0]==0) 
    p->quota=-1;  // read only
  else 
  {
    int quota;
    a2i(param, &quota);     //$ FA(17/07/2001)
    p->quota = quota*1024;
  }
  p->next=NULL;
  MMechostr(MSKTRACE,"partition %s %d\n",p->path,p->quota);
  
  return p;
}
*/
packdir SPcreatedir(char *name,char *param, int type, packdir last)
{
  int  c;
  packdir p;
  
  char err[1024];
  char curpath[MAX_PATH];
  if ((p = SPallocpackdir()) == NULL) 
  	return NULL;
  
  /*
  if ((!strncmp(name,"./",2))||(!strncmp(name,".\\",2)))
  {
	  sprintf(p->path,"%s%s",execpath,name+1);
  }
  else strcpy(p->path,name);
  */
  /*
  if ((!strncmp(name, "./", 2)) || (!strncmp(name, ".\\", 2)))
  	if ((type == 0) || (type == 1))								// Cache and Common App Data partitions
	  	sprintf(p->path, "%s%s", cappdatapath, name+1);
	  else																					// Local App Data partitions (Type = 2)
	  	sprintf(p->path, "%s%s", lappdatapath, name+1);
  else 
  	strcpy(p->path, name);
  */
  //$BB add stand alone mode
  if ((!strncmp(name, "./", 2)) || (!strncmp(name, ".\\", 2)))
  	if (type == 0) 																			// Cache
      sprintf(p->path, "%s%s", bStandAlone ? execpath : lappdatapath, name+1);
	  else if (type == 1)																	// User partition
	  	sprintf(p->path, "%s%s", bStandAlone ? execpath : mydcmtspath, name+1);
	  else																								// Locked App partition (Type = 2)
	  	sprintf(p->path, "%s%s", execpath, name+1);
  else 
  	strcpy(p->path, name);
  
  c = p->path[strlen(p->path)-1];
  if ((c != '/') && (c != '\\')) 
  	strcat(p->path, "/");
  
  //$BLG - Note: Scol partition directory in Local App Data isn't necessarily created during Voyager install (Multiple users case)
  //if (type == 2)
  //$BLG - Note: Scol partitions directories in Local App Data or My Documents aren't necessarily created during Voyager install (Multiple users case)
  if ((type == 0) || (type == 1))
  {
		#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
		 	GetCurrentDirectory(MAX_PATH, curpath);
			if (!SetCurrentDirectory(p->path))
			{
				if (!CreateDirectory(p->path, NULL))
				{
					sprintf(err, "Local partition directory creation failed\n%s", p->path);
					MessageBox(NULL, err, "SPcreatedir", MB_OK);
				}
			}
			SetCurrentDirectory(curpath);
		#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
			// TODO_LINUX
		#else
			// TODO_MAC
		#endif
	}
  
  if (param[0] == 0)
    p->quota = -1;  // read only
  else
  {
    int quota;
    a2i(param, &quota);     //$ FA(17/07/2001)
    //$BLG - v5.2.06: Modif (Information in usmuser.ini is now in MB, no more in KB)
    //p->quota = quota*1024;
    p->quota = quota*1024*1024;
  }
  p->next = NULL;
  
  if (p->quota == -1) 
  	MMechostr(MSKTRACE, "partition %s - Capacity: Read only\n", p->path);
  else if (p->quota == 0) 
  	MMechostr(MSKTRACE, "partition %s - Capacity : Unlimited size\n", p->path);
  else
  	MMechostr(MSKTRACE, "partition %s - Capacity: %d MB\n", p->path, p->quota / (1024*1024));
  
  return p;
}


/*
packdir SPinitpack()
{
  packdir p,q,first;
  int i;

  cookies[0]=0;
  if ((Cachepack)&&(Cachepack==Firstpack)) return Firstpack;
  first=p=NULL;

  for(i=0;i<ipart;i++)
  {
	  q=SPcreatedir(namepart[i],parampart[i],p);
	  if (q)
	  {
		  if (p) p->next=q;
		  else first=q;
		  p=q;
	  }
  }
  Cachepack=first;
  flagLicense=0;
  if (first)
  {
	  if (first->next) Firstpack=first->next;
	  else Firstpack=first;
  }
  return Firstpack;
}
*/
packdir SPinitpack()
{
  packdir p, q, first;
  int i;

  cookies[0] = 0;
  if ((Cachepack) && (Cachepack == Firstpack)) 
  	return Firstpack;
  
  first = p = NULL;

  for(i = 0; i < ipart; i++)
  {
	  q = SPcreatedir(namepart[i], parampart[i], typepart[i], p);
	  if (q)
	  {
		  if (p) p->next = q;
		  else first = q;
		  p = q;
	  }
  }
  Cachepack = first;		// <- First found 'disk' in usm.ini is Cache ... possibility to have only a Cache partition ...
  flagLicense = 0;
  if (first)
  {
	  if (first->next) 
	  	Firstpack = first->next;
	  else 
	  	Firstpack = first;
  }
  return Firstpack;
}

int SPisname(int c, int l)
{
  if ((c>='a')&&(c<='z')) return 1;
  if ((c>='A')&&(c<='Z')) return 1;
  if ((c>='0')&&(c<='9')) return 1;
  if ((c=='_')||(c=='-')) return 1;
  if ((c=='.')&&(l!='.')) return 1;
  if ((c=='\'')&&(l!='\'')) return 1;
  if ((c=='/')&&(l!='/')) return 1;
  if ((c=='~')&&(l!='/')) return 1;
  if ((c==' ')||((c&255)>127)) return 1;
  return 0;
}
 
 
/* calcul de typsign pour un nom de fichier 
   retourne -1 si erreur */
int SPgettypsign(char *total)
{
  int i,l;
  
  i=0;
  l='/';
  while((i<SIZESIGN)&&(SPisname(total[i],l))) l=total[i++];
  if (i>=SIZESIGN) return -1;
  if (total[i]==0) return TYPESNONE;
  else if (total[i]=='#') return TYPESLOGIC;
  else if (total[i]=='$') return TYPESENV;
  else if (total[i]=='&') return TYPESMACH;
  else if (total[i]==';') return TYPESCOOKIES;
  return -1;
}


/* decoupe le nom en clair d'un nom de fichier
   retourne -1 si incorrect, 0 sinon */
int SPgetname(char *total,char *name)
{
  int i,c,l;
  
  i=0;
  l='/';
  while((i<SIZESIGN)&&(SPisname(c=total[i],l))) name[i++]=l=c;
  if (i>=SIZESIGN) return -1;
  name[i]=0;
  return 0;
}


/* complete le chemin d'un package */
int SPrefinePack(packdir p,char *n)
{
	int i,l,c;

	l=strlen(n);
	if (!SPisname(n[0],0)) return -1;
	for(i=1;i<l;i++) if (!SPisname(n[i],n[i-1])) return -1;
	if (p==NULL) return -1;
	strcat(p->path,n);
	c=p->path[strlen(p->path)-1];
	if ((c!='/')&&(c!='\\')) strcat(p->path,"/");
	MMechostr(1," refine : %s\n",p->path);
	return 0;
}


int SPslashtoback(char *p)
{
	while(*p)
	{
		if ((*p)=='/') *p='\\';
		p++;
	}
	return 0;
}


int SPkillmaj(char *p)
{
	while(*p)
	{
		if (((*p)>='A')&&((*p)<='Z')) (*p)+=0x20;
		p++;
	}
	return 0;
}


/* trouve un package dans la liste des packdir */
int SPfindfile(packdir p,char *sign,int *size,char *path)
{
  int idx,typ,i;
  FILE *fd;
  char buf[SIZESIGN];
  char bufs[1024];

  if (path) path[0]=0;
  if (p==NULL)
  {
    if ((fd=fopen(sign,"rb"))==NULL)
      return -1;
    if (size)
  	{
	  	idx=0;
 	    while (fgetc(fd)!=EOF) idx++;
      *size=idx;
    }
    fclose(fd);
    if (path) strcpy(path,sign);
    return 0;
  }
  if ((typ=SPgettypsign(sign))<0)
    return -1;
  
  while(p)
  {
    sprintf(buf,"%s%s",p->path,sign);   
  	SPkillmaj(buf+strlen(p->path));
    if (fd=fopen(buf,"rb"))
    {		  
      if (typ==TYPESLOGIC)
		  {
				SCincSignInit();
				do
				{
					i=fread(bufs,1,1024,fd);
				    SCincSign(bufs,i,NULL);
				} while(i==1024);
				SPgetname(sign,bufs);
				strcat(bufs,"#");
				SCincSign(NULL,0,&bufs[strlen(bufs)]);
				MMechostr(MSKDEBUG,"test sign %s %s\n",sign,bufs);
				if (stricmp(sign,bufs))
					return -1;
		  }
	    if (size)
		  {
			  fseek(fd,0,SEEK_SET);
			  idx=0;
	      while (fgetc(fd)!=EOF) idx++;
			  *size=idx;
		  }
      fclose(fd);
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
	  	SPslashtoback(buf);
#endif
      // Log that package was found.
      MMechostr(0, "%s - %s - %s\n", p->path, sign, buf); 

      if (path) 
        strcpy(path,buf);

      return 0;
    }
    p=p->next;
  }
  return -1;
}


/* test l'existence des repertoires d'un chemin complet (repertoire+nom de fichier) */
int SPcheckdirandcreate(char *path)
{
	char buf[SIZESIGN*2];
	int i;

	strcpy(buf,path);
	i=0;
	while(buf[i])
	{
		if (buf[i]=='/')
		{
			buf[i]=0;
/*			MMechostr(MSKDEBUG,"create directory : %s\n",buf);*/
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
			_mkdir(buf);
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
			mkdir(buf,-1); 
#endif
#ifdef VERSION_MAC
			TODO
#endif
			buf[i]='/';
		}
		i++;
	}
	return 0;
}


int SPtestfilename(char *p)
{
	int f;
	f=0;
	while(*p)
	{
		if (((*p)=='.')||((*p)=='#')||((*p)==';')||((*p)=='$')||((*p)=='&')) f=1;
		if ((((*p)=='/')||((*p)=='\\'))&&(f)) return -1;
		p++;
	}
	if (f) return 0;
	return -1;
}


/* ajout d'un fichier : trouve le chemin complet a partir du nom complet et 
  de la taille */
int SPaddfile(packdir p,char *sign,int size,char *path)
{
  int typ;

  /* nouveau test sur les noms de fichiers dans le cache */
  if ((Firstpack==Cachepack)&&(SPtestfilename(sign))) return -1;

//MMechostr(0, "SPaddfile\n");

  if (path) path[0]=0;
  if (p==NULL)
  {
    MMechostr(0, "SPaddfile NULL\n");
    if (path) strcpy(path,sign);
    return 0;
  }
  if ((typ=SPgettypsign(sign))<0) return -1;

  while(p)
  {
    /* mettre une gestion fine des quotas */
    if (p->quota!=-1)
    {
      //MMechostr(0, "SPaddfile q:%d\n", p->quota);
      if (path)
		  {
			  //MMechostr(0, "SPaddfile path %s\n", p->path);
			  sprintf(path,"%s%s",p->path,sign);
			  SPkillmaj(path+strlen(p->path));
			  SPcheckdirandcreate(path);
		  }
      return 0;
    }
    p=p->next;
  }
  return -2;
}


/* Signature d'un fichier. version1.0 */
int SCsign(mmachine m,char *src,int n,char *name,int typesign,char *sign)
{
  char buf[32];
  //$BLG - v5.11: Modif
  int i,j,k,c,d,p;
  //int k, p;
  //ushort c, d, j;
/*
//Modifies only first char
sprintf(buf,"AA");
d=200;
buf[0]+=d;
MMechostr(0,"%s %d %d\n",buf,buf[0],buf[1]);
*/
  if (typesign==TYPESNONE)
  {
    strcpy(sign,name);
    return 0;
  }
  if (typesign==TYPESLOGIC)
  {
    //$BLG - v5.11: Optim
    
    for(i=0;i<=16;i++) buf[i]=0;
    d=j=0;
    for(i=0;i<n;i++)
    {
	  	c=src[i];
      for(k=0;k<16;k++)
      {
        buf[(j+k)&15]+=d;
        d=d*c+1;
        d=(d+(d>>8))&255;
      }
      j=(j-1)&15;
    }
    for(i=0;i<16;i++)
    {
      c=(buf[i]>>2)&31;
      if (c<10) c+='0'; else c+='a'-10;  
      buf[i]=c;
    }

    sprintf(sign,"%s#%s",name,buf);
    return 0;
  }
  if (typesign==TYPESENV)
  {  
    p=MMfetch(m,MMgetglobal(m,OFFSCCUR)>>1,OFFCHANENV);
    if (MMpush(m,p)) return MERRMEM;
    if (k=SCsignenv(m)) return k;
    sprintf(sign,"%s$%s",name,MMstartstr(m,MMpull(m)>>1));
    return 0;
  }
  if (typesign==TYPESMACH)
  {  
    if (k=SCsignmachine(m)) return k;
    sprintf(sign,"%s&%s",name,MMstartstr(m,MMpull(m)>>1));
    return 0;
  }
  if (typesign==TYPESCOOKIES)
  {  
    sprintf(sign,"%s;%s",name,cookies);
    return 0;
  }
  return -1;
}


/* Signature incrémentale . version1.0 */

char bufIncSign[32];
int lastIncSign;


int SCincSignInit()
{
	int i;
	
  for(i=0;i<=16;i++) bufIncSign[i]=0;
  lastIncSign=0;
  
	return 0;
}

int SCincSign(char *buf,int n,char *sign)
{
  int i,j,k,c;

  j=0;
  for(i=0;i<n;i++)
  {
	  c=buf[i];
	  for(k=0;k<16;k++)
	  {
		  bufIncSign[(j+k)&15]+=lastIncSign;
      lastIncSign=lastIncSign*c+1;
      lastIncSign=(lastIncSign+(lastIncSign>>8))&255;
    }
    j=(j-1)&15;
  }
  if (sign)
  {
	  for(i=0;i<16;i++)
    {
		  c=(bufIncSign[i]>>2)&31;
      if (c<10) c+='0'; else c+='a'-10;
      sign[i]=c;
	  }
	  sign[i]=0;
  }
  return 0;
}
