GrapevineUser (Cedar): sending mail
GVSendImpl.mesa
Andrew Birrell May 16, 1983 2:19 pm
DIRECTORY
GVBasics USING [ItemType, MakeKey, maxRNameLength, Password, RName],
GVLocate USING [FindNearestServer, FoundServerInfo],
GVProtocol USING [Close, CreateStream, Failed, Handle, ReceiveAck, ReceiveBoolean, ReceiveByte, ReceiveCount, ReceiveRName, SendBoolean, SendCount, SendMSOperation, SendNow, SendPassword, SendBytes, SendRName, SendRope],
GVSend USING [ExpandInfo, StartSendInfo],
PupDefs USING [PupAddress],
Rope USING [Length, ROPE];
GVSendImpl: CEDAR MONITOR
IMPORTS GVBasics, GVLocate, GVProtocol, Rope
EXPORTS GVSend =
BEGIN
Handle: TYPE = REF Object;
Object: PUBLIC TYPE = RECORD[
state: {idle, started, noItem, inItem} ← idle,
str: GVProtocol.Handle ← NIL ];
Create: PUBLIC PROC RETURNS[handle: Handle] =
BEGIN
handle ← NEW[Object];
END;
SendFailed: PUBLIC ERROR[why: Rope.ROPE, notDelivered: BOOLEAN] = CODE;
WrongCallSequence: ERROR = CODE;
-- cached server address --
serverKnown: BOOLEANFALSE;
serverAddr: PupDefs.PupAddress;
StartSend: PUBLIC PROC[handle: Handle,
senderPwd: Rope.ROPE,
sender: GVBasics.RName,
returnTo: GVBasics.RName ← NIL,
validate: BOOLEAN ]
RETURNS[ info: GVSend.StartSendInfo ] =
BEGIN
info ← SendFromClient[handle,
0, 0,
GVBasics.MakeKey[senderPwd],
sender,
IF returnTo = NIL THEN sender ELSE returnTo,
validate];
END;
SendFromClient: PUBLIC ENTRY PROC[handle: Handle,
fromNet: [0..256), fromHost: [0..256),
senderKey: GVBasics.Password,
sender: GVBasics.RName,
returnTo: GVBasics.RName,
validate: BOOLEAN ]
RETURNS[ info: GVSend.StartSendInfo ] =
BEGIN
ENABLE UNWIND => Close[handle];
IF handle.state # idle THEN ERROR WrongCallSequence[];
IF sender.Length[] > GVBasics.maxRNameLength THEN RETURN[badSender];
IF returnTo.Length[] > GVBasics.maxRNameLength THEN RETURN[badReturnTo];
DO handle.str ← InnerGetStream[];
IF handle.str=NIL THEN { info ← allDown; EXIT };
BEGIN
ENABLE GVProtocol.Failed => GOTO wentDown;
GVProtocol.SendMSOperation[handle.str, startSend];
GVProtocol.SendRName[handle.str, sender];
GVProtocol.SendPassword[handle.str, senderKey];
GVProtocol.SendRName[handle.str, returnTo];
GVProtocol.SendBoolean[handle.str, validate];
GVProtocol.SendNow[handle.str];
TRUSTED {info ← LOOPHOLE[GVProtocol.ReceiveByte[handle.str]]};
EXITS wentDown => { Close[handle]; serverKnown ← FALSE; LOOP };
END;
EXIT
ENDLOOP;
IF info = ok THEN handle.state ← started;
END;
GetStream: ENTRY PROC RETURNS[str: GVProtocol.Handle] = INLINE
{ str ← InnerGetStream[] };
InnerGetStream: INTERNAL PROC RETURNS[str: GVProtocol.Handle] =
BEGIN
str ← NIL;
IF NOT serverKnown
THEN GOTO noCache
ELSE str ← GVProtocol.CreateStream[serverAddr, MSSend ! GVProtocol.Failed => GOTO noCache];
EXITS noCache =>
BEGIN
Accept: PROC[addr: PupDefs.PupAddress] RETURNS[BOOLEAN] =
BEGIN
str ← GVProtocol.CreateStream[addr, MSSend ! GVProtocol.Failed => GOTO no];
serverKnown ← TRUE; serverAddr ← addr;
RETURN[TRUE];
EXITS no => RETURN[FALSE]
END;
server: GVLocate.FoundServerInfo =
GVLocate.FindNearestServer["Maildrop.MS", Accept];
IF server.t # found
THEN { IF str#NIL THEN GVProtocol.Close[str]; str ← NIL };
END;
END;
AddRecipient: PUBLIC ENTRY PROC[handle: Handle, recipient: GVBasics.RName] =
BEGIN
ENABLE
BEGIN
GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered: TRUE];
UNWIND => Close[handle];
END;
IF handle.state # started THEN ERROR WrongCallSequence[];
IF recipient.Length[] > GVBasics.maxRNameLength THEN recipient ← "Name too long";
GVProtocol.SendMSOperation[handle.str, addRecipient];
GVProtocol.SendRName[handle.str, recipient];
END;
CheckValidity: PUBLIC ENTRY PROC[handle: Handle,
notify: PROC[INT, GVBasics.RName] ]
RETURNS[ ok: INT ] =
BEGIN
ENABLE
BEGIN
GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state # started THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.str, checkValidity];
GVProtocol.SendNow[handle.str];
DO n: INT = GVProtocol.ReceiveCount[handle.str];
IF n = 0 THEN EXIT;
notify[n, GVProtocol.ReceiveRName[handle.str]];
ENDLOOP;
ok ← GVProtocol.ReceiveCount[handle.str];
handle.state ← noItem;
END;
StartItem: PUBLIC ENTRY PROC[handle: Handle, type: GVBasics.ItemType] =
BEGIN
ENABLE
BEGIN
GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state = started THEN handle.state ← inItem;
IF handle.state = inItem THEN handle.state ← noItem;
IF handle.state # noItem THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.str, startItem];
GVProtocol.SendCount[handle.str, LOOPHOLE[type]];
handle.state ← inItem;
END;
AddToItem: PUBLIC ENTRY PROC[handle: Handle,
buffer: Rope.ROPE ] =
BEGIN
ENABLE
BEGIN
GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state # inItem THEN ERROR WrongCallSequence[];
GVProtocol.SendMSOperation[handle.str, addToItem];
GVProtocol.SendCount[handle.str, buffer.Length[]];
GVProtocol.SendRope[handle.str, buffer];
END;
Send: PUBLIC ENTRY PROC[handle: Handle] =
BEGIN
ENABLE UNWIND => Close[handle];
IF handle.state = started THEN handle.state ← inItem;
IF handle.state # inItem THEN ERROR WrongCallSequence[];
-- SendNow to give better error if connection has gone away --
GVProtocol.SendNow[handle.str !
GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered:TRUE] ];
BEGIN
ENABLE GVProtocol.Failed => ERROR SendFailed[why: text, notDelivered:FALSE];
GVProtocol.SendMSOperation[handle.str, send];
GVProtocol.SendNow[handle.str];
GVProtocol.ReceiveAck[handle.str];
END;
Close[handle];
handle.state ← idle;
END;
Abort: PUBLIC ENTRY PROC[handle: Handle] =
{ Close[handle] };
Close: INTERNAL PROC[handle: Handle] =
BEGIN
IF handle.str # NIL THEN GVProtocol.Close[handle.str];
handle.str ← NIL;
handle.state ← idle;
END;
ExpandFailed: PUBLIC ERROR[why: Rope.ROPE] = CODE;
Expand: PUBLIC PROC[name: GVBasics.RName, work: PROC[GVBasics.RName]]
RETURNS[info: GVSend.ExpandInfo] =
BEGIN
str: GVProtocol.Handle = GetStream[];
IF str = NIL
THEN info ← allDown
ELSE BEGIN
ENABLE
BEGIN
GVProtocol.Failed => ERROR ExpandFailed[text];
UNWIND => GVProtocol.Close[str];
END;
IF Rope.Length[name] > GVBasics.maxRNameLength
THEN info ← notFound
ELSE BEGIN
GVProtocol.SendMSOperation[str, expand];
GVProtocol.SendRName[str, name];
GVProtocol.SendNow[str];
WHILE GVProtocol.ReceiveBoolean[str]
DO work[GVProtocol.ReceiveRName[str]] ENDLOOP;
info ← LOOPHOLE[GVProtocol.ReceiveByte[str]];
END;
GVProtocol.Close[str];
END;
END;
END.