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
DIRECTORY
Basics USING [ BITSHIFT, BITXOR ],
ConvertUnsafe USING [ ToRope ],
GVBasics USING [ GVString, maxGVStringLength, oldestTime, Password, RName, Timestamp],
GVProtocol USING [ bpw, FailureReason, GVSocket, Handle, ReceiveCount, ReceiveRC, ReceiveRName, ReceiveTimestamp, ReturnCode, RSOperation, SendCount, SendRName, SendRSOperation, SendTimestamp, StringSize ],
IO USING [ EndOfStream, Flush, GetChar, PutChar, PutFR, PutRope, STREAM, UnsafeBlock, UnsafeGetBlock, UnsafePutBlock ],
PupDefs USING [ AnyLocalPupAddress, PupPackageMake, SecondsToTocks ],
PupStream USING [ PupAddress, PupByteStreamCreate, StreamClosing, TimeOut ],
PupTypes USING [ Byte, fillInSocketID, PupAddress, PupSocketID ],
Rope USING [ Fetch, Length, ROPE ];
GVProtocolImpl:
CEDAR
MONITOR
IMPORTS Basics, ConvertUnsafe, GVProtocol, IO, PupDefs, PupStream, Rope
EXPORTS GVBasics, GVProtocol = {
OPEN GVProtocol;
MakeKey:
PUBLIC
PROC [password: Rope.
ROPE]
RETURNS [ key: GVBasics.Password ] = {
key ← ALL[0];
FOR i:
CARDINAL
IN
CARDINAL[0..password.Length[])
DO
j: [0..LENGTH[key]) = (i/bpw) MOD LENGTH[key];
c:
WORD =
LOOPHOLE[
IF password.Fetch[i]
IN ['A..'Z]
THEN password.Fetch[i] - 'A + 'a
ELSE password.Fetch[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 [Rope.
ROPE] = {
RETURN [ IO.PutFR["%b#%b@%g", [integer[stamp.net]], [integer[stamp.host]], [cardinal[stamp.time]] ] ];
};
bpw:
INT = GVProtocol.bpw;
SocketsArray:
TYPE =
ARRAY GVProtocol.GVSocket
OF PupTypes.PupSocketID;
sockets:
REF SocketsArray ←
NEW[SocketsArray ← [
none: [0,0],
RSEnquiry: [0, 50B],
RSPoll: [0, 52B],
Lily: [0, 53B],
MSPoll: [0, 54B],
MSForward: [0, 55B],
MSSend: [0, 56B],
MSRetrieve: [0, 57B]
]];
SetTestingMode:
PUBLIC
ENTRY
PROC = {
FOR s: GVProtocol.GVSocket IN GVProtocol.GVSocket DO sockets[s].a ← 1 ENDLOOP;
};
GetSocket:
PUBLIC
ENTRY
PROC [gv: GVSocket]
RETURNS [PupTypes.PupSocketID] = {
RETURN [ sockets[gv] ];
};
myAddr: PupTypes.PupAddress;
IsLocal:
PUBLIC
PROC [addr: PupTypes.PupAddress]
RETURNS [
BOOL] = {
RETURN [ addr.net = myAddr.net AND addr.host = myAddr.host ]
};
Failed:
PUBLIC
SIGNAL [why: FailureReason, text: Rope.
ROPE] =
CODE;
CreateStream:
PUBLIC
PROC [host: PupTypes.PupAddress, socket: GVProtocol.GVSocket, secs:
CARDINAL ← 120]
RETURNS [GVProtocol.Handle] = {
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
IF socket # none THEN host.socket ← sockets[socket];
RETURN [ PupStream.PupByteStreamCreate[host, PupDefs.SecondsToTocks[secs] ] ]
};
SendNow:
PUBLIC
PROC [str: GVProtocol.Handle] = {
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.Flush[];
};
SendByte:
PUBLIC
PROC [str: GVProtocol.Handle, byte: Byte] = {
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.PutChar[LOOPHOLE[byte]];
};
ReceiveByte:
PUBLIC
PROC [str: GVProtocol.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[str.GetChar[]];
};
SendBytes:
PUBLIC
PROC [str: GVProtocol.Handle, block:
IO.UnsafeBlock] = {
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.UnsafePutBlock[block];
};
ReceiveBytes:
PUBLIC
UNSAFE
PROC [str: GVProtocol.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 = str.UnsafeGetBlock[block];
IF nBytes # block.count
THEN ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""];
};
SendGVString:
PUBLIC
PROC [str: GVProtocol.Handle, rope: GVBasics.GVString] = {
length: INT = rope.Length[];
IF length > GVBasics.maxGVStringLength
THEN
ERROR Failed[clientError, "Excessive rope length"];
GVProtocol.SendCount[str, length]; GVProtocol.SendCount[str, 0--ignored--];
SendRope[str, rope];
IF length MOD 2 # 0 THEN SendByte[str,0];
};
ReceiveGVString:
PUBLIC
PROC [str: GVProtocol.Handle]
RETURNS [GVBasics.GVString] =
TRUSTED {
string: STRING = [GVBasics.maxGVStringLength];
string.length ← GVProtocol.ReceiveCount[str]; [] ← GVProtocol.ReceiveCount[str];
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: GVProtocol.Handle, rope: Rope.
ROPE] = {
str.PutRope[rope ! PupStream.StreamClosing => ERROR Failed[communicationError, text]];
};
Enquire:
PUBLIC
PROC [str: GVProtocol.Handle, op: GVProtocol.RSOperation, name: GVBasics.RName, oldStamp: GVBasics.Timestamp ← GVBasics.oldestTime]
RETURNS [rc: GVProtocol.ReturnCode, stamp: GVBasics.Timestamp] = {
GVProtocol.SendRSOperation[str, op];
GVProtocol.SendRName[str, name];
IF op IN [Expand .. CheckStamp] THEN GVProtocol.SendTimestamp[str, oldStamp];
SendNow[str];
rc ← GVProtocol.ReceiveRC[str];
IF rc.code = done
AND op
IN [Expand .. CheckStamp]
THEN stamp ← GVProtocol.ReceiveTimestamp[str]
ELSE stamp ← oldStamp;
};
ReceiveRList:
PUBLIC
PROC [str: GVProtocol.Handle, work:
PROC [GVBasics.RName]] = {
length: INT ← ReceiveCount[str];
WHILE length > 0
DO
name: GVBasics.RName = GVProtocol.ReceiveRName[str];
length ← length - GVProtocol.StringSize[name];
work[name];
ENDLOOP;
};
TRUSTED {
PupDefs.PupPackageMake[];
myAddr ← PupDefs.AnyLocalPupAddress[PupTypes.fillInSocketID];
};
}.