GrapevineUser (Cedar): sending mail
GVSendImpl.mesa
Hal Murray November 1, 1984 7:44:54 pm PST
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],
GVSendExtras USING [],
PupDefs USING [PupAddress],
Rope USING [Length, ROPE];
GVSendImpl:
CEDAR
MONITOR
IMPORTS GVBasics, GVLocate, GVProtocol, Rope
EXPORTS GVSend, GVSendExtras =
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;
Name for list of servers used to inject mail into the system
mailDrop: Rope.ROPE ← "MailDrop.MS";
cached server address
serverKnown: BOOLEAN ← FALSE;
serverAddr: PupDefs.PupAddress;
SetMailDropList:
PUBLIC
PROC [list: Rope.
ROPE] =
BEGIN
mailDrop ← list;
END;
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, 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.