GVProtocolImpl.mesa - subroutines for mail and reg server protocols
Copyright © 1985 by Xerox Corporation. All rights reserved.
Hal Murray May 27, 1985 9:12:23 pm PDT
Russ Atkinson (RRA) October 18, 1985 1:18:30 pm PDT
Hal Murray, May 12, 1986 10:02:01 pm PDT
DIRECTORY
Basics USING [BITSHIFT, BITXOR, bytesPerWord],
ConvertUnsafe USING [ToRope],
Endian USING [HWORD],
GVBasics USING [Connect, GVString, ItemHeader, maxGVStringLength, oldestTime, Password, Remark, RName, Timestamp],
GVNames USING [RSOperation],
GVProtocol USING [FailureReason, GVSocket, Handle, MSOperation, ReturnCode],
IO USING [Close, EndOfStream, Flush, GetChar, PutChar, PutFR, PutRope, STREAM, UnsafeBlock, UnsafeGetBlock, UnsafePutBlock],
IOExtras USING [GetHWord, PutHWord],
Pup USING [Address, nullSocket, Socket],
PupSocket USING [IsThisMe],
PupStream USING [Create, StreamClosing, Timeout],
PupWKS USING [gvLily, gvMSClientInput, gvMSForward, gvMSPoll, gvMSRetrieve, gvRSPoll, gvRSEnquiry],
Rope USING [Fetch, Length, ROPE];
GVProtocolImpl:
CEDAR
MONITOR
IMPORTS Basics, ConvertUnsafe, IO, IOExtras, PupSocket, PupStream, Rope
EXPORTS GVBasics, GVProtocol = {
Handle:
TYPE = GVProtocol.Handle;
MakeKey:
PUBLIC
PROC [password: Rope.
ROPE]
RETURNS [key: GVBasics.Password] = {
key ← ALL[0];
FOR i:
CARDINAL
IN
CARDINAL[0..Rope.Length[password])
DO
j: [0..LENGTH[key]) = (i/bpw) MOD LENGTH[key];
c:
WORD =
LOOPHOLE[
IF Rope.Fetch[password, i]
IN ['A..'Z]
THEN Rope.Fetch[password, i] - 'A + 'a
ELSE Rope.Fetch[password, i] ];
key[j] ← Basics.BITXOR[key[j], Basics.BITSHIFT[c, IF (i MOD 2) = 0 THEN 9 ELSE 1]];
ENDLOOP;
};
RopeFromTimestamp:
PUBLIC
PROC [stamp: GVBasics.Timestamp]
RETURNS [r: Rope.
ROPE] = {
r ← IO.PutFR["%b#%b@%g", [integer[stamp.net]], [integer[stamp.host]], [cardinal[stamp.time]]];
};
bpw:
INT = Basics.bytesPerWord;
SocketsArray:
TYPE =
ARRAY GVProtocol.GVSocket
OF Pup.Socket;
sockets:
REF SocketsArray ←
NEW[SocketsArray ← [
none: Pup.nullSocket,
RSEnquiry: PupWKS.gvRSEnquiry,
RSPoll: PupWKS.gvRSPoll,
Lily: PupWKS.gvLily,
MSPoll: PupWKS.gvMSPoll,
MSForward: PupWKS.gvMSForward,
MSSend: PupWKS.gvMSClientInput,
MSRetrieve: PupWKS.gvMSRetrieve
]];
SetTestingMode:
PUBLIC
ENTRY
PROC = {
FOR s: GVProtocol.GVSocket IN GVProtocol.GVSocket DO sockets[s].b ← 1 ENDLOOP;
};
GetSocket:
PUBLIC
ENTRY
PROC [gv: GVProtocol.GVSocket]
RETURNS [Pup.Socket] = {
RETURN[sockets[gv]];
};
IsLocal:
PUBLIC
PROC [addr: Pup.Address]
RETURNS [
BOOL] = {
RETURN[PupSocket.IsThisMe[addr]];
};
Failed:
PUBLIC
SIGNAL [why: GVProtocol.FailureReason, text: Rope.
ROPE] =
CODE;
CreateStream:
PUBLIC
PROC [host: Pup.Address, socket: GVProtocol.GVSocket, secs:
INT ← 120]
RETURNS [Handle] = {
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
IF socket # none THEN host.socket ← sockets[socket];
RETURN[PupStream.Create[host, secs*1000, secs*1000]];
};
SendNow:
PUBLIC
PROC [str: Handle] = {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not reading data"]; };
IO.Flush[str];
};
Close:
PUBLIC
PROC [str: Handle] = {
IO.Close[str];
};
SendAck:
PUBLIC
PROC [str: Handle] = {
SendByte[str, 0];
};
ReceiveAck:
PUBLIC
PROC [str: Handle] = {
[] ← ReceiveByte[str];
};
SendBoolean:
PUBLIC
PROC [str: Handle, bool:
BOOL] = {
SendByte[str, IF bool THEN 1 ELSE 0];
};
ReceiveBoolean:
PUBLIC
PROC [str: Handle]
RETURNS [
BOOL] = {
RETURN[ReceiveByte[str] # 0];
};
SendByte:
PUBLIC
PROC [str: Handle, byte: Byte] = {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not reading data"]; };
IO.PutChar[str, LOOPHOLE[byte]];
};
ReceiveByte:
PUBLIC
PROC [str: Handle]
RETURNS [byte: Byte] = {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not sending data"];
IO.EndOfStream => ERROR Failed[protocolError, "Unexpected \"mark\""]; };
byte ← LOOPHOLE[IO.GetChar[str]];
};
SendCount:
PUBLIC
PROC [str: Handle, count: Endian.
HWORD] =
TRUSTED {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not reading data"]; };
IOExtras.PutHWord[str, count];
};
ReceiveCount:
PUBLIC
PROC [str: Handle]
RETURNS [count: Endian.
HWORD] =
TRUSTED {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not sending data"];
IO.EndOfStream => ERROR Failed[protocolError, "Unexpected \"mark\""]; };
count ← IOExtras.GetHWord[str];
};
SendItemHeader:
PUBLIC
PROC [str: Handle, header: GVBasics.ItemHeader] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@header]], 0, bpw*SIZE[GVBasics.ItemHeader]]];
};
ReceiveItemHeader:
PUBLIC
PROC [str: Handle]
RETURNS [header: GVBasics.ItemHeader] =
TRUSTED {
ReceiveBytes[str, [
LOOPHOLE[
LONG[@header]], 0, bpw*
SIZE[GVBasics.ItemHeader]]];
};
SendTimestamp:
PUBLIC
PROC [str: Handle, stamp: GVBasics.Timestamp] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@stamp]], 0, bpw*SIZE[GVBasics.Timestamp]]];
};
ReceiveTimestamp:
PUBLIC
PROC [str: Handle]
RETURNS [stamp: GVBasics.Timestamp] =
TRUSTED {
ReceiveBytes[str, [LOOPHOLE[LONG[@stamp]], 0, bpw*SIZE[GVBasics.Timestamp]]];
};
SendPassword:
PUBLIC
PROC [str: Handle, pw: GVBasics.Password] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@pw]], 0, bpw*SIZE[GVBasics.Password]]];
};
ReceivePassword:
PUBLIC
PROC [str: Handle]
RETURNS [pw: GVBasics.Password] =
TRUSTED {
ReceiveBytes[str, [LOOPHOLE[LONG[@pw]], 0, bpw*SIZE[GVBasics.Password]]];
SendRC:
PUBLIC
PROC [str: Handle, rc: GVProtocol.ReturnCode] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@rc]], 0, bpw*SIZE[GVProtocol.ReturnCode]]];
};
ReceiveRC:
PUBLIC
PROC [str: Handle]
RETURNS [rc: GVProtocol.ReturnCode] =
TRUSTED {
ReceiveBytes[str, [LOOPHOLE[LONG[@rc]], 0, bpw*SIZE[GVProtocol.ReturnCode]]];
};
SendMSOperation:
PUBLIC
PROC [str: Handle, op: GVProtocol.MSOperation] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@op]], 0, bpw*SIZE[GVProtocol.MSOperation]]];
};
ReceiveMSOperation:
PUBLIC
PROC [str: Handle]
RETURNS [op: GVProtocol.MSOperation] =
TRUSTED {
ReceiveBytes[str, [LOOPHOLE[LONG[@op]], 0, bpw*SIZE[GVProtocol.MSOperation]]];
};
SendRSOperation:
PUBLIC
PROC [str: Handle, op: GVNames.RSOperation] =
TRUSTED {
SendBytes[str, [LOOPHOLE[LONG[@op]], 0, bpw*SIZE[GVNames.RSOperation]]];
};
ReceiveRSOperation:
PUBLIC
PROC [str: Handle]
RETURNS [op: GVNames.RSOperation] =
TRUSTED {
ReceiveBytes[str, [LOOPHOLE[LONG[@op]], 0, bpw*SIZE[GVNames.RSOperation]]];
};
SendBytes:
PUBLIC
PROC [str: Handle, block:
IO.UnsafeBlock] = {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not reading data"]; };
IO.UnsafePutBlock[str, block];
};
ReceiveBytes:
PUBLIC
UNSAFE
PROC [str: Handle, block:
IO.UnsafeBlock] =
TRUSTED {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not sending data"];
IO.EndOfStream => ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""]; };
nBytes: INT = IO.UnsafeGetBlock[str, block];
IF nBytes # block.count
THEN
ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""];
};
StringSize:
PUBLIC
PROC [string: GVBasics.GVString]
RETURNS [
INT] = {
RETURN[2 + (Rope.Length[string] + bpw-1) / bpw];
};
SendRName:
PUBLIC
PROC [str: Handle, name: GVBasics.RName] = {
SendGVString[str, name];
ReceiveRName:
PUBLIC
PROC [str: Handle]
RETURNS [GVBasics.RName] = {
RETURN[ReceiveGVString[str]];
SendConnect:
PUBLIC
PROC [str: Handle, connect: GVBasics.Connect] = {
SendGVString[str, connect];
ReceiveConnect:
PUBLIC
PROC [str: Handle]
RETURNS [GVBasics.Connect] = {
RETURN[ReceiveGVString[str]];
SendRemark:
PUBLIC
PROC [str: Handle, remark: GVBasics.Remark] = {
SendGVString[str, remark];
ReceiveRemark:
PUBLIC
PROC [str: Handle]
RETURNS [GVBasics.Remark] = {
RETURN[ReceiveGVString[str]];
SendGVString:
PUBLIC
PROC [str: Handle, rope: GVBasics.GVString] = {
length: INT = rope.Length[];
IF length > GVBasics.maxGVStringLength
THEN
ERROR Failed[clientError, "Excessive rope length"];
SendCount[str, length];
SendCount[str, 0--ignored--];
SendRope[str, rope];
IF length MOD 2 # 0 THEN SendByte[str,0];
};
ReceiveFunnyString:
PUBLIC
PROC [str: Handle]
RETURNS [GVBasics.GVString] =
TRUSTED {
string: STRING = [GVBasics.maxGVStringLength];
string.length ← ReceiveCount[str];
No maxLength
ReceiveBytes[str, [LOOPHOLE[LONG[@string.text]], 0, bpw*(SIZE[StringBody[string.length]] - SIZE[StringBody[0]])]];
RETURN[ConvertUnsafe.ToRope[string]];
};
ReceiveGVString:
PUBLIC
PROC [str: Handle]
RETURNS [GVBasics.GVString] =
TRUSTED {
string: STRING = [GVBasics.maxGVStringLength];
string.length ← ReceiveCount[str];
[] ← ReceiveCount[str]; -- maxLength
IF string.length > string.maxlength
THEN
ERROR Failed[protocolError, "Protocol error: too many characters"];
ReceiveBytes[str, [LOOPHOLE[LONG[@string.text]], 0, bpw*(SIZE[StringBody[string.length]] - SIZE[StringBody[0]])]];
RETURN [ConvertUnsafe.ToRope[string]]
};
SendRope:
PUBLIC
PROC [str: Handle, rope: Rope.
ROPE] = {
ENABLE {
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.Timeout => SIGNAL Failed[noData, "Server not reading data"]; };
IO.PutRope[str, rope];
};
Enquire:
PUBLIC
PROC [str: Handle, op: GVNames.RSOperation, name: GVBasics.RName, oldStamp: GVBasics.Timestamp ← GVBasics.oldestTime]
RETURNS [rc: GVProtocol.ReturnCode, stamp: GVBasics.Timestamp] = {
SendRSOperation[str, op];
SendRName[str, name];
IF op IN [Expand .. CheckStamp] THEN SendTimestamp[str, oldStamp];
SendNow[str];
rc ← ReceiveRC[str];
IF rc.code = done AND op IN [Expand .. CheckStamp] THEN stamp ← ReceiveTimestamp[str]
ELSE stamp ← oldStamp;
};
ReceiveRList:
PUBLIC
PROC [str: Handle, work:
PROC [GVBasics.RName]] = {
length: INT ← ReceiveCount[str];
WHILE length > 0
DO
name: GVBasics.RName = ReceiveRName[str];
length ← length - StringSize[name];
work[name];
ENDLOOP;
};
}.