GrapevineUser (Cedar) - subroutines for mail and reg server protocols
GVProtocolImpl.mesa
Andrew Birrell  23-Jan-81 17:07:47
Last Edited by: Levin, September 22, 1983 11:59 am
Last Edited by: Birrell, August 25, 1983 5:06 pm
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 =
 
BEGIN
OPEN GVProtocol;
MakeKey: 
PUBLIC 
PROC[password: Rope.
ROPE] 
RETURNS[ key: GVBasics.Password ] =
BEGIN
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;
END;
 
RopeFromTimestamp: 
PUBLIC 
PROC[stamp: GVBasics.Timestamp] 
RETURNS[Rope.
ROPE] =
{ 
RETURN[ 
IO.PutFR["%b#%b@%g",
[integer[stamp.net]], [integer[stamp.host]], [integer[stamp.time]] ] ] };
 
 
bpw: INT = GVProtocol.bpw;
sockets: 
ARRAY GVProtocol.GVSocket 
OF PupTypes.PupSocketID ← [
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 
PROCEDURE =
BEGIN
FOR s: GVProtocol.GVSocket IN GVProtocol.GVSocket DO sockets[s].a ← 1 ENDLOOP;
END;
 
GetSocket: 
PUBLIC 
ENTRY 
PROC[gv: GVSocket] 
RETURNS[PupTypes.PupSocketID] =
{ RETURN[ sockets[gv] ] };
 
myAddr: PupTypes.PupAddress;
IsLocal: 
PUBLIC 
PROCEDURE[addr: PupTypes.PupAddress] 
RETURNS[ 
BOOLEAN ] =
BEGIN
RETURN[ addr.net = myAddr.net AND addr.host = myAddr.host ]
END;
 
Failed: PUBLIC SIGNAL[why: FailureReason, text: Rope.ROPE] = CODE;
CreateStream: 
PUBLIC 
PROCEDURE[host: PupTypes.PupAddress, socket: GVProtocol.GVSocket, secs: 
CARDINAL ← 120 ]
RETURNS[ GVProtocol.Handle ] =
BEGIN
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
IF socket # none THEN host.socket ← sockets[socket];
RETURN[ PupStream.PupByteStreamCreate[host, PupDefs.SecondsToTocks[secs] ] ]
END;
 
SendNow: 
PUBLIC 
PROCEDURE[ str: GVProtocol.Handle ] =
BEGIN
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.Flush[];
END;
 
Byte: TYPE = [0..256);
SendByte: 
PUBLIC 
PROCEDURE[ str: GVProtocol.Handle, byte: Byte ] =
BEGIN
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.PutChar[LOOPHOLE[byte]];
END;
 
ReceiveByte: 
PUBLIC 
PROCEDURE[ str: GVProtocol.Handle ]
RETURNS[ byte: Byte ] =
BEGIN
ENABLE
BEGIN
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.TimeOut => SIGNAL Failed[noData, "Server not sending data"];
IO.EndOfStream => ERROR Failed[protocolError, "Unexpected \"mark\""];
END;
 
byte ← LOOPHOLE[str.GetChar[]];
END;
 
SendBytes: 
PUBLIC 
PROC[str: GVProtocol.Handle, block: 
IO.UnsafeBlock] =
BEGIN
ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text];
str.UnsafePutBlock[block];
END;
 
ReceiveBytes: 
PUBLIC 
UNSAFE 
PROC[str: GVProtocol.Handle, block: 
IO.UnsafeBlock] = 
TRUSTED
BEGIN
ENABLE
BEGIN
PupStream.StreamClosing => ERROR Failed[communicationError, text];
PupStream.TimeOut => SIGNAL Failed[noData, "Server not sending data"];
IO.EndOfStream => ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""];
END;
 
nBytes: INT = str.UnsafeGetBlock[block];
IF nBytes # block.count
THEN ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""];
END;
 
SendGVString: 
PUBLIC 
PROC[ str: GVProtocol.Handle, rope: GVBasics.GVString] =
BEGIN
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];
END;
 
ReceiveGVString: 
PUBLIC 
PROC[ str: GVProtocol.Handle] 
RETURNS[GVBasics.GVString] = 
TRUSTED
BEGIN
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]]
END;
 
SendRope: 
PUBLIC 
PROC[ str: GVProtocol.Handle, rope: Rope.
ROPE] =
BEGIN
str.PutRope[rope ! PupStream.StreamClosing => ERROR Failed[communicationError, text]];
END;
 
Enquire: 
PUBLIC 
PROCEDURE[str: GVProtocol.Handle,
op: GVProtocol.RSOperation,
name: GVBasics.RName,
oldStamp: GVBasics.Timestamp ← GVBasics.oldestTime ]
RETURNS[ rc: GVProtocol.ReturnCode, stamp: GVBasics.Timestamp ] =
BEGIN
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;
END;
 
ReceiveRList: 
PUBLIC 
PROCEDURE[str: GVProtocol.Handle,
work: PROCEDURE[GVBasics.RName] ] =
BEGIN
length: INT ← ReceiveCount[str];
WHILE length > 0
DO name: GVBasics.RName = GVProtocol.ReceiveRName[str];
length ← length - GVProtocol.StringSize[name];
work[name];
 
ENDLOOP;
END;
 
Init: 
PROC = 
TRUSTED
BEGIN
PupDefs.PupPackageMake[];
myAddr ← PupDefs.AnyLocalPupAddress[PupTypes.fillInSocketID];
END;
 
Init[];
END.