    /*********************************************************************\
    *  Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu)   *
    *                                                                     *
    *  You may copy or modify this file in any manner you wish, provided  *
    *  that this notice is always included, and that you hold the author  *
    *  harmless for any loss or damage resulting from the installation or *
    *  use of this software.                                              *
    \*********************************************************************/

#include "server_def.h"

#ifdef VMS
# define MOREDOTS         /* remove this if you don't want more than 1 dot
                             in a filename */
/* Use UNIX emulation functions */
# define O_CREAT 0x0200
# define fopen vms_fopen
# define open vms_open
# define access vms_access
# define STAT vms_stat
# define link rename

  int dchanged=0;   /* flag which shows if .fsp_content needs to be updated */
  int isvar=0;      /* flag: file is in variable record format */
#else
# define STAT stat
#endif

FPCACHE *cache_p, fpcache[FSP_FILE_CACHE+1]; /* file cache */
extern FPCACHE *find_cache();
extern FPCACHE *add_cache();

extern char *readme_file;

/*****************************************************************************
* This is a modified server_file that hash file names.			     *
*****************************************************************************/

#define NBSIZE (2*sizeof(UBUF))

#define fexist(A) (!access(A,0))
#define touch(A) close(open(A,O_CREAT,0777))
#define LF_PFX '&'
#define LP_PFX '\\'

int server_check_pass(ownerfile, password)
char *ownerfile, *password;
{
    FILE *fp;
    char fsp_passwd[80];

    if(dbug) printf("check_pass: opening file %s\n", ownerfile);
    fp = fopen(ownerfile, "r");
    if(fscanf(fp, "%s", fsp_passwd) != 1)
    {
	if(dbug) printf("check_pass: no password in ownerfile\n");
	fclose(fp);
	return 0;
    }
    fclose(fp);
    if(dbug) printf("Check_pass: read %s, check with %s\n", fsp_passwd,
		    password);
    return strcmp(fsp_passwd, password);
}

/*****************************************************************************
* Routine to check a path string given by the client.
* Will replace null string by ".".
* In case of error, returns the error string.
*****************************************************************************/

/*****************************************************************************
*  The PPATH structure is filled in by the function check_path when given a
*  path string.  The elements are filled in as such:
* 
*	fullp      pointer to a string containing the full path name
*	f_ptr      pointer to begining of the last component of the path
*       f_len      length of the last component of the path
*	d_ptr      pointer to begining of the directory part of path
*       d_len      length of the directory part of the path
*
*  fullp is a null-terminated full path string.
*  f_ptr is always a null-terminated sub-string of fullp.
*  p_ptr is generally not null-terminated.
*****************************************************************************/

char *check_path(fullp,len,pp)
    char *fullp;
    int   len;
    PPATH *pp;
{
    char *s;
    int state;

    if(len < 1) return("Path must have non-zero length");
    if(fullp[len-1]) return("Path not null terminated");

    pp->d_ptr = "."; pp->d_len = 1;	/* initial dir part ---> root */
    pp->passwd = "\0";			/* default, no password */

    if(len == 1 && fullp[0] == 0)	/* null path --> root */
    {
	pp->fullp = pp->f_ptr = ".";
	pp->f_len = 1;
	return(NULLP);
    }

    for(s = pp->fullp = pp->f_ptr = fullp, state = 0; *s; s++)
    {
	if(*s == '\n')
	{
	    pp->passwd = s+1; *s = '\0';
	    if(dbug) printf("check_path: found password field %s\n", s+1);
        }
	else if(*s <= ' ' || *s >= '~') return("Path contains illegal chars");

	switch(*s)
	{
	    case LF_PFX:
	    case LP_PFX:
	    case    '.': if(!state) return("Path can't begin with '.'");
#ifdef MOREDOTS
                         else *s='$'; /* convert dots to '$' */
#endif
			 break;

	    case    '/': if(!state) return("Path can't contain '//'");
			 pp->d_ptr = fullp;
			 pp->d_len = s - fullp;
			 pp->f_ptr = s+1;
			 state = 0;
			 break;

	    default    : state = 1;
			 break;
	}
    }

    pp->f_len = s - pp->f_ptr;
    return(NULLP);
}

/*****************************************************************************
* Put the directory part of the path pp into dbuf.
* return pointer to the null character.
*****************************************************************************/

char *copy_dir(dbuf,pp)
    char *dbuf;
    PPATH *pp;
{
    char *p1,*p2;
    int cnt;

    for(p1 = dbuf, p2 = pp->d_ptr, cnt = pp->d_len; cnt--; *p1++ = *p2++);
    *p1 = 0; return(p1);
}

/*****************************************************************************
* Reads directory and write directory listing file.
*****************************************************************************/

static build_dir_file(fp)
    FILE *fp;
{
    int nlen, skip, rem;
    DIR *dir_f;
#ifdef DIRENT
    struct dirent *dp;
#else
    struct direct *dp;
#endif
    struct stat    sb;
    register char  *s;
    RDIRENT rdb;
    static char marker[] = "******************";
    char longname[UBUF_SPACE];
    FILE *lfp;
#ifdef VMS
    char *ext;         /* pointer to strip .dir extension */
#endif

    if(!(dir_f = opendir(".")))	/* assume I have cd to path already */
	{ fprintf(stderr,"Can't open dir during initialization\n"); exit(1); }

#ifdef VMS
    /* VMS doesn't display the current directory as a dot in the list 
     * so create a dummy dotfile (maybe this should be included in the
     * readdir() emulation function) */

    rem = UBUF_SPACE;
    nlen = 2;
    rdb.type = RDTYPE_DIR;
    rdb.size = htonl(1024);
    rdb.time = htonl(time((time_t *)0)); /* get current time for curr. dir */
    fwrite(&rdb,1,RDHSIZE,fp);
    fwrite(".",1,nlen,fp);

    rem -= (nlen + RDHSIZE);

    if((skip = (nlen + RDHSIZE) & 0x3))
		             { fwrite(&rdb,1,4-skip,fp); rem -= (4-skip); }

    /* now collect the files in the directory */
    for( 1 ; dp = readdir(dir_f); )
    {
        ext = dp->d_name;
        while (*ext=tolower(*ext)) ext++; /* kludge to convert filename to
                                       lowercase */
        s = dp->d_name;
        if (*(ext = s+strlen(s)-1) == '.') *ext=0; /* strip last dot */
#else
    for(rem = UBUF_SPACE; dp = readdir(dir_f); )
    {
	if (dp->d_ino == 0) continue;
#endif
	s = dp->d_name;

	if(s[0] == LF_PFX)
	{
	    s[0] = LP_PFX;
	    if(!(lfp = fopen(s,"r")))
		{ unlink(s); *s = LF_PFX; unlink(s); continue; }
	    fgets(longname,UBUF_SPACE,lfp);  fclose(lfp);
	    s[0] = LF_PFX; s = longname; longname[UBUF_SPACE-1] = 0;
	}

	if(s[0] == LP_PFX) continue;

						/* hide dot files */
	if((s[0] == '.') && ((s[1] !=  0 ) &&
			     (s[1] != '.' || s[2] != 0))) continue;

	if(stat(dp->d_name,&sb)) continue;

#ifdef VMS  /* strip .DIR extension */
        if (sb.st_mode & S_IFDIR) if (ext=strstr(s,".dir")) *ext=0;
# ifdef MOREDOTS /* replace '$' with dots */
        while (s=strstr(s,"$")) *s++='.'; s=dp->d_name;
# endif
#endif /* VMS */

	nlen = strlen(s)+1;

	if(rem < RDHSIZE + nlen)
	{
	    rdb.type = RDTYPE_SKIP;
	    if(rem <= RDHSIZE) { fwrite(marker,1,rem    ,fp); }
			  else { fwrite(marker,1,RDHSIZE,fp);
				 fwrite(s, 1,rem-RDHSIZE,fp); }
	    rem = UBUF_SPACE;
	}

	rdb.type = ((S_IFDIR & sb.st_mode)) ? RDTYPE_DIR : RDTYPE_FILE;
	rdb.size = htonl(sb.st_size);
	rdb.time = htonl(sb.st_mtime);

	fwrite((char *) &rdb,1,RDHSIZE,fp);
	fwrite(s,1,nlen,fp);
	rem -= (nlen + RDHSIZE);

	if((skip = (nlen + RDHSIZE) & 0x3))
		{ fwrite((char *) &rdb,1,4-skip,fp); rem -= (4-skip); }

	if(!rem) rem = UBUF_SPACE;
    }

    rdb.type = RDTYPE_END;
    fwrite((char *) &rdb,1,RDHSIZE,fp);

    fflush(fp);
    closedir(dir_f);
}

/***************************************************************************/

#ifdef VMS
/* the problem on VMS-systems is, that the directory-date doesn't change
 * when anything is changed in it, so we need a manual update of the
 * .FSP_CONTENT file after a upload,deletion,etc.
 */
char *server_update_dir(pp,inet_num)
PPATH *pp;
unsigned long inet_num;
{
    FILE *fp;  /* dummy file, for server_get_dir() */
    char pathname[NBSIZE],*oldpath, *errmsg = "\0";

    dchanged=1; /* mark that fsp_content has been changed */
    copy_dir(pathname,pp); /* strip filename from path. */
    oldpath=pp->fullp;pp->fullp=pathname; /* new path, backup old one */
    if(!(errmsg=server_get_dir(pp,inet_num,&fp)))
	fclose(fp); /* update .FSP_CONTENT */
    pp->fullp=oldpath;  /* restore old path */
    return(errmsg);
}
#endif /* VMS */

/***************************************************************************/

char *server_get_dir(pp,inet_num,fp)
    PPATH *pp;			/* returned file may not point at BOF */
    unsigned long inet_num;     /* user validation for .FSP_PRIVATE */
    FILE **fp;                  /* returned filepointer to .FSP_CONTENT */
{
    char *server_get_dir_2();
    struct stat sd, sf;
    char   list_p[NBSIZE],owner_p[NBSIZE],private_p[NBSIZE];
    int    fsp_content_exists;
#ifdef VMS
    char dirname[NBSIZE];

    /* now the stupid dirname conversion for stat(), I have to look for a
     * unix-like stat() for VMS.
     * I can't make the conversion in stat(), because I don't know
     * when a path is a full-dir or a dir with a filename on the end.
     */

    if (*pp->fullp=='.') getcwd(dirname,512,0);
    else strcpy(dirname,pp->fullp);
    strcat(dirname,".dir.1"); /* for here, add .DIR.1 for directory */

    if(STAT(dirname,&sd)) return("Can't find directory");

    if(!(S_IFDIR & sd.st_mode)) return("Not a directory");

    sprintf(owner_p,"%s/.OWN%08X"  ,pp->fullp,inet_num);
#else
    if(stat(pp->fullp,&sd)) return("Can't find directory");
    if(!(S_IFDIR & sd.st_mode)) return("Not a directory");

    sprintf(owner_p,"%s/.OWN.%08X"  ,pp->fullp,inet_num);
#endif
    sprintf(list_p,"%s/.FSP_CONTENT",pp->fullp);
    sprintf(private_p,"%s/.FSP_PRIVATE", pp->fullp);

    if(!fexist(owner_p) && fexist(private_p))
			return("This directory is private");

    if(fsp_content_exists = !STAT(list_p,&sf))
    {
#ifdef VMS
        if (!dchanged) /* VMS doesn't change the modification-date of a
                          directory when the contents has been changed */
#else
	if(sf.st_mtime >= sd.st_mtime)
#endif
	{
	    *fp = fopen(list_p,"r");
	    if(!*fp) return("Can't read directory listing");
	    return((char *)0);
	}
    }

    if(!fsp_content_exists &&
	always_use_cache_dir) return(server_get_dir_2(pp,fp,&sd));

#ifdef VMS
    unlink(list_p); /* remove old .FSP_CONTENT first (otherwise new file) */
#endif

    if(!(*fp = fopen(list_p,"w+")))
    {
	if(!fsp_content_exists &&
	   !always_use_cache_dir) return(server_get_dir_2(pp,fp,&sd));
	return("directory unreadable");
    }
    if(chdir(pp->fullp)) return("Can't cd to directory");

    build_dir_file(*fp);
    if(chdir(home_dir) == -1) { perror("chdir2"); exit(1); }
#ifdef VMS
    dchanged=0;  /* .fsp_content now up to date */
#endif
    return(NULLP);
}

char *server_get_dir_2(pp,fp,sd)
    PPATH *pp;
    FILE **fp;
    struct stat *sd;
{
    struct stat sf;
    char   name_p[NBSIZE], list_p[NBSIZE], tmp[UBUF_SPACE];
    unsigned int hash, i, mask, new_namefile;
    unsigned char *p;
    FILE *np;
#ifdef VMS
    char   pathname[NBSIZE];  /* for conversion of pp->fullp */
#endif

    mask = dir_cache_limit << 8;

    for(hash = 0, p = (unsigned char *) pp->fullp; *p; p++)
    {
	hash = (hash << 1) ^ *p;
	if(hash & mask) hash ^= (mask+1);
    }

    for(i = 8; i--; )
    {
	hash <<= 1;
	if(hash & mask) hash ^= (mask+1);
    }

    hash = (hash >> 8) & (mask-1);

#ifdef VMS   /* dir_cache_dir is already in VMS format */
    sprintf(name_p,"%s.N%x",dir_cache_dir,hash);
    sprintf(list_p,"%s.L%x",dir_cache_dir,hash);
#else
    sprintf(name_p,"%s/.N%x",dir_cache_dir,hash);
    sprintf(list_p,"%s/.L%x",dir_cache_dir,hash);
#endif

    if(!(np = fopen(name_p,"r"))) new_namefile = 1; else
    {
	fgets(tmp,UBUF_SPACE,np); fclose(np);

	if(strcmp(tmp,pp->fullp)) new_namefile = 1; else
	{
#ifdef VMS
            if(!STAT(list_p,&sf) && (!dchanged))
            /* VMS doesn't change the modification-date of a directory
               when the contents has been changed */
#else
	    if(!STAT(list_p,&sf) && (sf.st_mtime >= sd->st_mtime))
#endif
	    {
		*fp = fopen(list_p,"r");
		if(!*fp) return("Can't read directory listing");
		return((char *)0);
	    }
	    new_namefile = 0;
	}
    }

    if(new_namefile)
    {
	if(!(np = fopen(name_p,"w")))
	    return("Can't write directory listing name file");
	fputs(pp->fullp,np); fclose(np);
    }
#ifdef VMS
    unlink(list_p); /* erase old list first */
#endif

    if(!(*fp = fopen(list_p,"w+"))) return("directory unreadable");
    if(chdir(pp->fullp)) return("Can't cd to directory");
    build_dir_file(*fp);
    if(chdir(home_dir) == -1) { perror("chdir2"); exit(1); }
#ifdef VMS
    dchanged=0; /* .fsp_content now up to date */
#endif
    return(NULLP);
}


/**********************************************************************/

char *server_del_file(pp,inet_num)		/* assume path is validated */
    PPATH *pp;
    unsigned long inet_num;
{
    int is_long;
    struct stat sb;
    char   ok_del_p[NBSIZE], owner_p[NBSIZE];

    if(is_long = (pp->f_len > max_nlen)) fold_path(pp);

    if(STAT(pp->fullp,&sb)) return("unlink: file not accessible");
    if(!(S_IFREG & sb.st_mode)) return("unlink: not an ordinary file");

#ifdef VMS  /* more than 2 dots not allowed in filenames */
    sprintf(copy_dir( owner_p,pp),"/.OWN%08X"  ,inet_num);
#else
    sprintf(copy_dir( owner_p,pp),"/.OWN.%08X"  ,inet_num);
#endif
     strcpy(copy_dir(ok_del_p,pp),"/.FSP_OK_DEL"         );

    if(!fexist(owner_p) && !fexist(ok_del_p))
			return("no permission for removing this file");

    if(unlink(pp->fullp) == -1) return("unlink: cannot unlink");

    if(is_long) { *pp->f_ptr = LP_PFX; unlink(pp->fullp); }

#ifdef VMS
    server_update_dir(pp,inet_num);  /* update .FSP_CONTENT */
#endif
    return(NULLP);
}

/**********************************************************************/

char *server_del_dir(pp,inet_num)
    PPATH *pp;
    unsigned long inet_num;
{
    struct stat sb;
    char   list_p[NBSIZE], ok_del_p[NBSIZE], ok_add_p[NBSIZE], owner_p[NBSIZE];
    char   private_p[NBSIZE], ok_mkdir_p[NBSIZE];
    int    has_ok_del_p, has_ok_add_p, has_private_p, has_mkdir_p;
#ifdef VMS
    char   dirname[NBSIZE];

    /* Again the kludge for stat() */
    strcpy(dirname,pp->fullp);
    strcat(dirname,".dir.1"); /* for here, add .DIR.1 for directory */

    if(STAT(dirname,&sb)) return("rmdir: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("rmdir: not an ordinary directory");

    /* more than 2 dots not allowed in filenames */
    sprintf( owner_p,"%s/.OWN%08X"   ,pp->fullp,inet_num);

#else
    if(STAT(pp->fullp,&sb)) return("rmdir: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("rmdir: not an ordinary directory");

    sprintf( owner_p,"%s/.OWN.%08X"   ,pp->fullp,inet_num);
#endif

    sprintf(    list_p,"%s/.FSP_CONTENT" ,pp->fullp         );
    sprintf(  ok_del_p,"%s/.FSP_OK_DEL"  ,pp->fullp         );
    sprintf(  ok_add_p,"%s/.FSP_OK_ADD"  ,pp->fullp         );
    sprintf( private_p,"%s/.FSP_PRIVATE" ,pp->fullp         );
    sprintf(ok_mkdir_p,"%s/.FSP_OK_MKDIR",pp->fullp         );


    if(!fexist(owner_p)) return("no permission for removing this directory");

    unlink(owner_p); unlink(list_p);
    has_ok_del_p    = !unlink(  ok_del_p);
    has_ok_add_p    = !unlink(  ok_add_p);
    has_private_p   = !unlink( private_p);
    has_mkdir_p     = !unlink(ok_mkdir_p);

#ifdef VMS
    if(rmdir(dirname) != 0)
#else
    if(rmdir(pp->fullp) != 0)
#endif
    {
	if( has_ok_del_p)    touch(  ok_del_p);
	if( has_ok_add_p)    touch(  ok_add_p);
        if(has_private_p)    touch( private_p);
        if( has_mkdir_p)     touch(ok_mkdir_p);
	if(     owner_p)     touch(   owner_p);
	return("rmdir: cannot unlink");
    }

#ifdef VMS
    server_update_dir(pp,inet_num); /* update .FSP_CONTENT */
#endif
    return(NULLP);
}

/**********************************************************************/

char *server_make_dir(pp,inet_num)
    PPATH *pp;
    unsigned long inet_num;
{
    char ok_add_p[NBSIZE], owner_p[NBSIZE], private_p[NBSIZE];

#ifdef VMS /* more than 2 dots not allowed in filenames */
    sprintf(copy_dir( owner_p,pp),"/.OWN%08X"  ,inet_num);
#else
    sprintf(copy_dir( owner_p,pp),"/.OWN.%08X"  ,inet_num);
#endif
     strcpy(copy_dir(ok_add_p,pp),"/.FSP_OK_MKDIR");

    if(!fexist(owner_p) && !fexist(ok_add_p))
			return("no permission for directory creation");

    /* make directory and place ownerfile in it */

#ifdef VMS
    sprintf(owner_p,  "%s/.OWN%08X",pp->fullp,inet_num);
    if(mkdir(pp->fullp,0) != 0) return("Can't create directory");
    server_update_dir(pp,inet_num); /* update .FSP_CONTENT */
#else
    sprintf(owner_p,"%s/.OWN.%08X",pp->fullp,inet_num);
    if(mkdir(pp->fullp,0777) != 0) return("Can't create directory");
#endif
    strcpy(copy_dir(private_p,pp),"/.FSP_PRIVATE");
    if (fexist(private_p))  /* subdir in a private dir is also private */
    { sprintf(private_p,"%s/.FSP_PRIVATE",pp->fullp);
      touch(private_p);
    }
    touch(owner_p);
    return(NULLP);
}

/**********************************************************************/

char *server_get_file(pp,fp,inet_num, port_num)
    PPATH *pp;
    FILE **fp;
    unsigned long inet_num;
    unsigned short port_num;
{
    struct stat sb;
    char   private_p[NBSIZE], owner_p[NBSIZE];
    FPCACHE *cache_f;
#ifdef VMS
# ifdef MOREDOTS
    char   *dot;
# endif
#endif

    if(pp->f_len > max_nlen) fold_path(pp);

#ifdef MOREDOTS
    if(STAT(pp->fullp,&sb))
    { /* file not found, try without $-conversion (for linked directories) */
      if (dot=strstr(pp->fullp,"$")) *dot++='.'; /* conv. $ back to dot */
      if (STAT(pp->fullp,&sb)) return("Can't find file");
    }
#else
   if (STAT(pp->fullp,&sb)) return("Can't find file");
#endif

    if(!(S_IFREG & sb.st_mode)) return("Not a file");

#ifdef VMS  /* more than one dot not allowed in filenames */
    isvar = isvariable(sb); /* return file format type*/
#endif
    if(!(cache_f = find_cache(fpcache, port_num, inet_num, pp->fullp)))
    /* file not found in cache? */
    {
	/* clear current cache-entry first */
	if(dbug) printf("get_file: going to delete cache_p: %d\n", cache_p);
	delete_cache(cache_p);
#ifdef VMS
	sprintf(copy_dir(  owner_p,pp),"/.OWN%08X", inet_num);
#else
	sprintf(copy_dir(  owner_p,pp),"/.OWN.%08X", inet_num);
#endif
	strcpy(copy_dir(private_p,pp),"/.FSP_PRIVATE"       );

	if(!fexist(owner_p) && fexist(private_p))
                        return("no permisson for reading this file");

	if(fexist(private_p))
	{
	    /* if(!pp->passwd[0]) return("You need an FSP_PASSWORD"); */
	    if(server_check_pass(owner_p, pp->passwd))
		return("Invalid FSP_PASSWORD");
	}

	/* open new file */
	if(!(*fp = fopen(pp->fullp,"r"))) return("Can't open file");

	/* add it to the file-cache */
	cache_p = add_cache(fpcache,cache_p,port_num,inet_num,pp->fullp,*fp);
    }
    else *fp = cache_f->fp; /* get filepoint from cache */
	
    return(NULLP);
}

/**********************************************************************/

char *server_get_pro(pp,result,inet_num) /* result and pp->fullp may overlap */
    PPATH *pp;
    char *result;
    unsigned long inet_num;
{
    struct stat sb;
    FILE *readme;
    char   buf[NBSIZE], proflags=0;
    char   buf1[UBUF_SPACE];
    unsigned len, num;
#ifdef VMS
    char dirname[NBSIZE];

    /* And again the kludge with the dirname conversion for stat() */
    if (*pp->fullp=='.') getcwd(dirname,512,0);
    else strcpy(dirname,pp->fullp);
    strcat(dirname,".dir.1"); /* for here, add .DIR.1 for directory */

    if(STAT(dirname,&sb)) return("getpro: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("getpro: not an ordinary directory");

    sprintf(buf,"%s/.OWN%08X",pp->fullp,inet_num);
#else
    if(STAT(pp->fullp,&sb)) return("getpro: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("getpro: not an ordinary directory");

    sprintf(buf,"%s/.OWN.%08X",pp->fullp,inet_num);
#endif

    if(fexist(buf)) proflags |= DIR_OWNER;
    sprintf(buf,"%s/.FSP_OK_DEL"   ,pp->fullp);
    if(fexist(buf)) proflags |= DIR_DEL;
    sprintf(buf,"%s/.FSP_OK_ADD"   ,pp->fullp);
    if(fexist(buf)) proflags |= DIR_ADD;
    sprintf(buf,"%s/.FSP_PRIVATE"  ,pp->fullp);
    if(fexist(buf)) proflags |= DIR_PRIV;
    sprintf(buf,"%s/.FSP_OK_MKDIR" ,pp->fullp);
    if(fexist(buf)) proflags |= DIR_MKDIR;

    sprintf(buf,"%s/%s", pp->fullp, readme_file);

    sprintf(result,
	    "Servers 2.6.6.3 and above encode this information.\n\n");

    len = strlen(result);
    /* the README file is opened with efopen() -- probably not necessary */
    if((readme = fopen(buf,"r")) != 0) {
	/* Note, this number might have to be hand tweaked later. */
        num = fread(buf1, sizeof(char), UBUF_SPACE-len, readme);
        buf1[num] = '\0';
        fclose(readme);
        strcat(result, buf1);
    }
    result[strlen(result)] = '\0';
    result[strlen(result)+1] = proflags;
    return(NULLP);
}

/**********************************************************************/

char *server_set_pro(pp,key,inet_num)
    PPATH *pp;
    char *key;
    unsigned long inet_num;
{
    struct stat sb;
    char   buf[NBSIZE];
#ifdef VMS
    char dirname[NBSIZE];

    /* And again the kludge with the dirname conversion for stat() */
    if (*pp->fullp=='.') getcwd(dirname,512,0);
    else strcpy(dirname,pp->fullp);
    strcat(dirname,".dir.1"); /* for here, add .DIR.1 for directory */

    if(STAT(dirname,&sb)) return("getpro: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("getpro: not an ordinary directory");

    sprintf(buf,"%s/.OWN%08X",pp->fullp,inet_num);
#else
    if(STAT(pp->fullp,&sb)) return("getpro: directory not accessible");
    if(!(S_IFDIR & sb.st_mode)) return("getpro: not an ordinary directory");

    sprintf(buf,"%s/.OWN.%08X",pp->fullp,inet_num);
#endif
    if(!fexist(buf)) return("no permission for changing the protection-mode");

    switch(key[1])
    {
	case 'c': sprintf(buf,"%s/.FSP_OK_ADD"  ,pp->fullp); break;
	case 'd': sprintf(buf,"%s/.FSP_OK_DEL"  ,pp->fullp); break;
        case 'p': sprintf(buf,"%s/.FSP_PRIVATE" ,pp->fullp); break;
        case 'm': sprintf(buf,"%s/.FSP_OK_MKDIR",pp->fullp); break;
	default : return("bad flag");
    }

    switch(key[0])
    {
	case '+':  touch(buf); break;
	case '-': unlink(buf); break;
	default : return("bad flag");
    }

    return(NULLP);
}

/**********************************************************************
*  These two are used for file uploading.
**********************************************************************/

char *server_up_load(data,len,pos,inet_num,port_num)
    char *data;
    int    len;
    unsigned long pos, inet_num;
    unsigned short port_num;
{
    FILE *fp;
    char  tname[NBSIZE];
    FPCACHE *cache_f;

#ifdef VMS  /* dir_cache_dir is already in VMS format */
    sprintf(tname,"%sT%08X%04X",dir_cache_dir,inet_num,port_num);
#else
    sprintf(tname,"%s/.T%08X%04X",dir_cache_dir,inet_num,port_num);
#endif

    if(!(cache_f = find_cache(fpcache, port_num, inet_num, tname)))
    /* file not found in cache? */
    {
	/* clear out current cache-entry first */
	delete_cache(cache_p);
	if (pos) { if(fp = fopen(tname, "r+")) fseek(fp, pos, 0); }
	else { unlink(tname); fp = fopen(tname,"w"); }

	if(!fp) return("Cannot open temporary file");

	fwrite(data, 1, len, fp);
	/* add it to the file cache */
	cache_p = add_cache(fpcache,cache_p,port_num,inet_num,tname,fp);
    }
    else /* file found in cache, using cache-fp */
    {
	fp = cache_f->fp;
	if(pos!=ftell(fp)) fseek(fp, pos, 0);
	fwrite(data, 1, len, fp);
    }
    return(NULLP);
}

char *server_install(pp,inet_num,port_num)
    PPATH *pp;
    unsigned long inet_num;
    unsigned short port_num;
{
    FILE *ft, *fp;
    char      tname[NBSIZE],  owner_p[NBSIZE], save_p[NBSIZE], buf[512];
    char   ok_del_p[NBSIZE], ok_add_p[NBSIZE];
    int bytes, is_long;
    FPCACHE *cache_f;
#ifdef VMS
    char filename[NBSIZE];
# ifdef MOREDOTS
    char *dot; /* pointer to a dot in a filename */
# endif
#endif

    if(is_long = (pp->f_len > max_nlen))
			{ strcpy(save_p,pp->f_ptr); fold_path(pp); }

#ifdef VMS
    sprintf(copy_dir(owner_p,pp),"/.OWN%08X",inet_num);
    sprintf(tname,"%sT%08X%04X",dir_cache_dir,inet_num,port_num);
#else
    sprintf(copy_dir(owner_p,pp),"/.OWN.%08X",inet_num);
    sprintf(tname,"%s/.T%08X%04X",dir_cache_dir,inet_num,port_num);
#endif
    /* if file still in cache, then close it & remove it from cache */
    if(cache_f = find_cache(fpcache,port_num, inet_num, tname))
	delete_cache(cache_f);

#ifdef MOREDOTS
    while (dot=strstr(pp->fullp,".")) *dot++='$'; /* replace dots with '$' */
#endif
    if (dbug) printf("server_install: owner_p: %s, tname: %s, pp->fullp: %s\n",
                      owner_p, tname, pp->fullp);
    if(!fexist(owner_p))
    {
	strcpy(copy_dir(ok_add_p,pp),"/.FSP_OK_ADD");
	strcpy(copy_dir(ok_del_p,pp),"/.FSP_OK_DEL");

	if(!fexist(ok_add_p))
	{
	    unlink(tname);
	    return("no permission for creating that file");
	}

	if(!fexist(ok_del_p) && fexist(pp->fullp))
	{
	    unlink(tname);
	    return("no permission for replacing that file");
	}
    }

    unlink(pp->fullp);
    if(link(tname,pp->fullp) == 0) { unlink(tname); goto done; }

    /* link failed, so just copy the temp file */
    if(!(ft = fopen(tname,"r"))) { unlink(tname);
				   return("Can't open temporary file"); }

    if(!(fp = fopen(pp->fullp,"w"))) { unlink(tname); fclose(ft);
				   return("Can't open file for output"); }
    /* copy temporary file to actual fput file */
    while(bytes = fread(buf,1,sizeof(buf),ft)) fwrite(buf,1,bytes,fp);

    fclose(ft); fclose(fp); unlink(tname);

  done:

    if(is_long)
    {
	*pp->f_ptr = LP_PFX;
	if(!(fp = fopen(pp->fullp,"w")))
	{
	    *pp->f_ptr = LF_PFX; unlink(tname);
	    return("Can't create long named file for output");
	}
	fputs(save_p,fp); fclose(fp);
    }

#ifdef VMS
    server_update_dir(pp,inet_num); /* update .FSP_CONTENT */
#endif
    return(NULLP);
}

/**********************************************************************/

char *server_secure_file(pp,inet_num,port_num)	/* assume path is validated */
    PPATH *pp;
    unsigned long  inet_num;
    unsigned short port_num;
{
    int is_long;
    struct stat sb;
    char   ok_del_p[NBSIZE], owner_p[NBSIZE], temp_p[NBSIZE];
    char   private_p[NBSIZE];

    if(is_long = (pp->f_len > max_nlen)) fold_path(pp);
    if(STAT(pp->fullp,&sb)) return("grab: file not accessible");
    if(!(S_IFREG & sb.st_mode)) return("grab: not an ordinary file");

#ifdef VMS /* more than one dot not allowed */
    sprintf(copy_dir( owner_p,pp),"/.OWN%08X"  ,inet_num         );
#else
    sprintf(copy_dir( owner_p,pp),"/.OWN.%08X" ,inet_num         );
#endif
     strcpy(copy_dir( ok_del_p,pp),"/.FSP_OK_DEL"                  );
     strcpy(copy_dir(private_p,pp),"/.FSP_PRIVATE"                 );
    sprintf(copy_dir(   temp_p,pp),"/.G%08X%04X" ,inet_num,port_num);

    if(!fexist(owner_p) && (!fexist(ok_del_p) || fexist(private_p)))
			return("grab: no permission for grabbing this file");

    if (fexist(private_p))
    {
	/* if (!pp->passwd[0]) return("You need an FSP_PASSWORD"); */
	if (server_check_pass(owner_p,pp->passwd))
	    return("Invalid FSP_PASSWORD");
    }

    unlink(temp_p);
    /* link on VMS emulated as a filecopy */
    if(link(pp->fullp,temp_p) == -1) return("grab: cannot make link");
    if(unlink(pp->fullp) == -1) { unlink(temp_p);
				  return("grab: cannot unlink"); }

    if(is_long) { *pp->f_ptr = LP_PFX; unlink(pp->fullp); }

    return(NULLP);
}

char *server_grab_file(pp,fp,inet_num,port_num)
    PPATH *pp;
    FILE **fp;
    unsigned long  inet_num;
    unsigned short port_num;
{
    struct stat sb;
    char temp_p[NBSIZE];

    sprintf(copy_dir(temp_p,pp),"/.G%08X%04X",inet_num,port_num);

    if(STAT(temp_p,&sb)) return("grab: can't find file");
    if(!(S_IFREG & sb.st_mode)) return("grab: Not a file");
    if(!(*fp = fopen(temp_p,"r"))) return("grab: can't open file");
    return(NULLP);
}

char *server_grab_done(pp,inet_num,port_num)
    PPATH *pp;
    unsigned long  inet_num;
    unsigned short port_num;
{
    struct stat sb;
    char temp_p[NBSIZE];

    sprintf(copy_dir(temp_p,pp),"/.G%08X%04X",inet_num,port_num);
    if(STAT(temp_p,&sb)) return("grab: can't find temporary file");
    if(unlink(temp_p) == -1) return("grab: can't delete temporary file");
#ifdef VMS
    server_update_dir(pp,inet_num); /* update .FSP_CONTENT */
#endif
    return(NULLP);
}

/**********************************************************************/

fold_path(pp)
    PPATH *pp;
{
    unsigned char *p;
    unsigned long v1, v2;

    p = (unsigned char *) pp->f_ptr;

    for(v1 = 0; v2 = *p++; )
    {
	if(v1 & 0x80000000) { v1 = (v1 << 1) ^ v2 ^ 1; }
		       else { v1 = (v1 << 1) ^ v2    ; }
    }

    sprintf(pp->f_ptr,"%c%08X",LF_PFX,v1);
    pp->f_len = 9;
}

/**********************************************************************/

init_home_dir()
{
#ifndef VMS
    if(*home_dir != '/') { fprintf(stderr,"[%s] does not start with a /\n");
			   exit(1); }
#endif
    if(chdir(home_dir) == -1) { perror("chdir"); exit(1); }

    if(dbug) { fprintf(stderr,"home on %s\n",home_dir); fflush(stderr); }
}

/***************************************************************************/
