
/*
 *  SGCOPY.C	 V1.1
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  GET-COPY SERVER	(NEW COPY SERVER)
 *
 *  The current version only accepts one connection at a time.	This server
 *  will send requested files to the remote machine.
 *
 *  length in 68000 longword format.
 *
 *  ACCESS RESTRICTIONS:
 *
 *	Remote access restrictions are according to DNET_GROUP or DNET_READ
 *
 *	DNET_READ >= 9	    Full Access
 *	DNET_READ <  9	    According to comment field (AC=n) or group acc.
 *			    (GR=n) matches DNET_GROUP)
 *
 *	When searching for access rights, the comment field of the file/dir
 *	requested is checked first.  If empty, the parent dir is checked.
 *	This continues (parent diring) until root is found or a comment
 *	field with access info in it.
 */

#include "defs.h"

typedef struct {
    char    Cmd;
    char    Str[64];
    long    Val;
} HDR;

extern int Enable_Abort;
char Buf[8192];
void *Chan;

void SGCopy ARGS((void));
int PutObject ARGS((char *));
int PutDir ARGS((char *, int));
int PutFile ARGS((char *, int));
int WriteHeader ARGS((char, char *, long));
int ReadHeader ARGS((HDR *));

int
brk()
{
    return(0);
}

void
#ifdef LATTICE
_main(str)
#else
_main(len,str)
#endif
char *str;
{
    struct MsgPort *port;
    PROC    *myproc = (PROC *)FindTask(NULL);
    long    savedir;
    long    mask, rmask;

    onbreak(brk);

    if (strncmp(str, "__dnet", 6) != 0) {
	Version("SGCopy", VERSION, SGCOPY_VERSION);
	_exit(0);
    }

    port = DListen(PORT_GFILECOPY);
    WaitPort(&myproc->pr_MsgPort);
    ReplyMsg(GetMsg(&myproc->pr_MsgPort));
    savedir = Lock("", SHARED_LOCK);   /* duplicate current dir    */
    if (!savedir) {
	DUnListen(port);
	_exit(1);
    }
    savedir = CurrentDir(savedir);              /* CD dup, returns original */
    mask = SIGBREAKF_CTRL_C|(1 << port->mp_SigBit);
    for (;;) {
	long dupdir = DupLock(myproc->pr_CurrentDir);
	rmask = Wait(mask);
	if (rmask & SIGBREAKF_CTRL_C) {
	    UnLock(CurrentDir(dupdir));
	    break;
	}
	while (Chan = DAccept(port)) {
	    SGCopy();
	    DClose(Chan);
	}
	UnLock(CurrentDir(dupdir));
    }
    UnLock(CurrentDir(savedir));                /* restore original         */
    DUnListen(port);
}

void
SGCopy()
{
    short error;
    static HDR Hdr;

    error = WriteHeader('H', "Hello, GCopy server V1.30", 0);
    if (error)
	return;
    switch(ReadHeader(&Hdr)) {
    case -1:
	return;
    case 'H':
	break;
    }
    while (!error) {
	switch(ReadHeader(&Hdr)) {
	case 'G':
	    error = PutObject(Hdr.Str);
	    break;
	case 'E':
	    goto done;
	case 'P':   /*  put-files, not implemented  */
	default:
	    error = 1;
	    break;
	}
    }
done:
    ;
}

int
PutObject(str)
char *str;
{
    long lock;
    FIB *fib = malloc(sizeof(FIB));
    short error = 0;
    short access = GetEnvVal(DNET_READ);
    short group  = GetEnvVal(DNET_GROUP);

    mountrequest(0);
    lock = Lock(str, SHARED_LOCK);
    mountrequest(1);
    if (lock == NULL || !Examine(lock, fib)) {     /*  unable to find it!  */
	error = WriteHeader('N', "Unable to find object", 0);
	goto done;
    }

    /*
     *	Determine Access rights
     *
     *	If the file/dir or any parent dir has AC > your_ac, access is
     *	denied.  If the file/dir and all parent dirs have neither an ACcess
     *	or GRoup entry, access is denied.  If a group entry is found that
     *	matches your group, access is allowed (unless an AC is found > your_ac)
     *
     *	If your_ac is >= 9, no access checking of parent directories is
     *	done at all.  However, access checking of downloaded files will
     *	be done, and any AC values > your_ac will be disalloweds.
     */

    if (access < 9) {           /*  must check comment/access   */
	short ok = 0;
	long lock2 = DupLock(lock);
	long tmp;
	for (;;) {
	    short ac = 0;

	    while (ac >= 0) {       /*  check groups    */
		short idx = 0;
		ac = ExtractFieldVal(fib->fib_Comment, FS_GROUP, &idx);
		if (ac >= 0 && ac == group) {
		    ok = 1;
		    break;
		}
	    }
	    ac = ExtractFieldVal(fib->fib_Comment, FS_ACCESS, NULL);
	    if (ac >= 0) {                  /*  valid access field      */
		if (ac <= access) {         /*  access ok               */
		    ok = 1;
		} else {		    /*	access not ok		*/
		    ok = 0;
		    break;
		}
	    }
	    if (tmp = ParentDir(lock2)) {   /*  check the par.dir */
		UnLock(lock2);
		lock2 = tmp;
		fib->fib_Comment[0] = 0;
		Examine(lock2, fib);
		continue;
	    }
	    break;
	}
	UnLock(lock2);
	if (!ok) {
	    error = WriteHeader('N', "Access Violation", 0);
	    goto done;
	}
    }
    Examine(lock, fib);
    UnLock(lock);
    lock = NULL;
    if (fib->fib_DirEntryType > 0) {
	error = PutDir(str, access);
    } else {
	error = PutFile(str, access);
    }
done:
    if (lock)
	UnLock(lock);
    free(fib);
    return((int)error);
}

int
PutDir(name, access)
char *name;
int access;
{
    long lock = Lock(name, SHARED_LOCK);
    FIB *fib = malloc(sizeof(FIB));
    static HDR Hdr;
    short error = 0;
    short ac;

    if (!lock || !Examine(lock, fib)) {
	WriteHeader('N', "Possible Disk Error", 0);
	error = 1;
	goto done;
    }

    ac = ExtractFieldVal(fib->fib_Comment, FS_ACCESS, NULL);
    if (ac >= 0 && ac > access) {
	error = WriteHeader('S', fib->fib_FileName, 0);
	goto done;
    }

    if (error = WriteHeader('D', fib->fib_FileName, 0))
	goto done;
    switch(ReadHeader(&Hdr)) {
    case 'Y':
	break;
    case 'S':
	goto done;
    case 'N':
	error = 1;
	break;
    default:
	error = 1;
	break;
    }
    if (error)
	goto done;
    while (ExNext(lock, fib)) {     /*  try to give him the files   */
	if (fib->fib_DirEntryType > 0) {
	    long oldlock = CurrentDir(lock);
	    error = PutDir(fib->fib_FileName, access);
	    CurrentDir(oldlock);
	    if (error)
		break;
	} else {
	    long oldlock = CurrentDir(lock);
	    error = PutFile(fib->fib_FileName, access);
	    CurrentDir(oldlock);
	    if (error)
		break;
	}
    }
    if (!error)
	WriteHeader('E', "Possible Disk Error", 0);
done:
    free(fib);
    if (lock)
	UnLock(lock);
    return((int)error);
}

int
PutFile(name, access)
char *name;
int access;
{
    long lock = Lock(name, SHARED_LOCK);
    long fh = NULL;
    FIB *fib = malloc(sizeof(FIB));
    static HDR Hdr;
    long len;
    short error = 0;
    short ac;

    if (!lock || !Examine(lock, fib)) {
	WriteHeader('N', "Possible Disk Error", 0);
	error = 1;
	goto done;
    }

    ac = ExtractFieldVal(fib->fib_Comment, FS_ACCESS, NULL);
    if (ac >= 0 && ac > access) {
	error = WriteHeader('S', fib->fib_FileName, 0);
	goto done;
    }

    fh = Open(name, 1005);
    if (fh == NULL)         /*  don't do anything if unable to open it */
	goto done;
    Seek(fh, 0L, 1);
    len = Seek(fh, 0L, 0);
    if (error = WriteHeader('F', fib->fib_FileName, len))
	goto done;
    switch(ReadHeader(&Hdr)) {
    case 'Y':
	Seek(fh, Hdr.Val, -1);  /*  start pos.  */
	len -= Hdr.Val;
	if (len < 0)
	    len = 0;
	break;
    case 'S':
	goto done;
    case 'N':
	error = 1;
	break;
    default:
	error = 1;
	break;
    }
    if (error)
	goto done;
    while (len) {
	long n = (len > sizeof(Buf)) ? sizeof(Buf) : len;

	if (Read(fh, Buf, n) != n) {    /*  read failed! */
	    error = 10;
	    goto done;
	}
	if (DWrite(Chan, Buf, n) != n) {
	    error = 10;
	    goto done;
	}
	len -= n;
    }
done:
    free(fib);
    if (fh)
	Close(fh);
    if (lock)
	UnLock(lock);
    return((int)error);
}

int
WriteHeader(c, str, len)
char c;
char *str;
long len;
{
    ubyte sl;

    if (str == NULL)
	str = "";
    sl = strlen(str);

    if (DWrite(Chan, &c, 1) < 0)
	return(1);
    if (DWrite(Chan, &sl,1) < 0)
	return(1);
    if (DWrite(Chan, str, sl) != sl)
	return(1);
    if (DWrite(Chan, &len, 4) != 4)
	return(1);
    return(0);
}

int
ReadHeader(hdr)
HDR *hdr;
{
    ubyte sl;
    ubyte cmd;

    hdr->Cmd = -1;
    if (DRead(Chan, &cmd, 1) != 1)
	return(-1);
    if (DRead(Chan, &sl, 1) != 1)
	return(-1);
    if (sl >= sizeof(hdr->Str)) {
	return(-1);
    }
    if (DRead(Chan, hdr->Str, sl) != sl)
	return(-1);
    hdr->Str[sl] = 0;
    if (DRead(Chan, &hdr->Val, 4) != 4)
	return(-1);
    hdr->Cmd = cmd;
    return((int)hdr->Cmd);
}


