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[];
};
Byte: TYPE = [0..256);
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];
};
}.