-- Transport Mechanism: User: sending mail
-- [Juniper]<Grapevine>User>SendMail.mesa
-- Andrew Birrell 30-Mar-81 13:44:59
DIRECTORY
BodyDefs USING [ItemType, maxRNameLength, Password, RName],
LocateDefs USING [FindNearestServer, FoundServerInfo],
ProtocolDefs USING [CreateStream, DestroyStream, Failed, Handle,
mailServerInputSocket, MakeKey, ReceiveAck,
ReceiveBoolean, ReceiveByte, ReceiveCount,
ReceiveRName, SendBoolean, SendCount,
SendMSOperation, SendNow, SendPassword, SendRName],
PupDefs USING [PupAddress],
PupStream USING [StreamClosing],
SendDefs USING [ExpandInfo, StartSendInfo],
Storage USING [Free, Node],
Stream USING [Handle, PutBlock];
SendMail: MONITOR
IMPORTS LocateDefs, ProtocolDefs, PupStream, Stream, Storage
EXPORTS SendDefs =
BEGIN
Handle: PUBLIC TYPE = POINTER TO HandleObject;
HandleObject: TYPE = RECORD[
state: {idle, started, noItem, inItem},
str: ProtocolDefs.Handle ];
Create: PUBLIC PROC RETURNS[handle: Handle] =
BEGIN
handle ← Storage.Node[SIZE[HandleObject]];
handle↑ ← [state: idle, str: NIL];
END;
Destroy: PUBLIC ENTRY PROC[handle: Handle] =
{ Close[handle]; Storage.Free[handle]};
SendFailed: PUBLIC ERROR[notDelivered: BOOLEAN] = CODE;
WrongCallSequence: ERROR = CODE;
-- cached server address --
serverKnown: BOOLEAN ← FALSE;
serverAddr: PupDefs.PupAddress;
StartSend: PUBLIC PROC[handle: Handle,
senderPwd: STRING,
sender: BodyDefs.RName,
returnTo: BodyDefs.RName ← NIL,
validate: BOOLEAN ]
RETURNS[ info: SendDefs.StartSendInfo ] =
BEGIN
info ← SendFromClient[handle,
0, 0,
ProtocolDefs.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: BodyDefs.Password,
sender: BodyDefs.RName,
returnTo: BodyDefs.RName,
validate: BOOLEAN ]
RETURNS[ info: SendDefs.StartSendInfo ] =
BEGIN
ENABLE UNWIND => Close[handle];
IF handle.state # idle THEN ERROR WrongCallSequence[];
DO handle.str ← InnerGetStream[];
IF handle.str=NIL THEN { info ← allDown; EXIT };
BEGIN
ENABLE ProtocolDefs.Failed => GOTO wentDown;
ProtocolDefs.SendMSOperation[handle.str, startSend];
ProtocolDefs.SendRName[handle.str, sender];
ProtocolDefs.SendPassword[handle.str, senderKey, [0,0,0,0]];
ProtocolDefs.SendRName[handle.str, returnTo];
ProtocolDefs.SendBoolean[handle.str, validate];
ProtocolDefs.SendNow[handle.str];
info ← LOOPHOLE[ProtocolDefs.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: ProtocolDefs.Handle] = INLINE
{ str ← InnerGetStream[] };
InnerGetStream: INTERNAL PROC RETURNS[str: ProtocolDefs.Handle] =
BEGIN
str ← NIL;
IF NOT serverKnown
THEN GOTO noCache
ELSE str ← ProtocolDefs.CreateStream[serverAddr !
ProtocolDefs.Failed => GOTO noCache];
EXITS noCache =>
BEGIN
Accept: PROC[addr: PupDefs.PupAddress] RETURNS[BOOLEAN] =
BEGIN
addr.socket ← ProtocolDefs.mailServerInputSocket;
str ← ProtocolDefs.CreateStream[addr !
ProtocolDefs.Failed => GOTO no];
serverKnown ← TRUE; serverAddr ← addr;
RETURN[TRUE];
EXITS no => RETURN[FALSE]
END;
server: LocateDefs.FoundServerInfo =
LocateDefs.FindNearestServer["Maildrop.MS"L, Accept];
IF server.t # found
THEN { IF str#NIL THEN ProtocolDefs.DestroyStream[str]; str ← NIL };
END;
END;
AddRecipient: PUBLIC ENTRY PROC[handle: Handle, recipient: BodyDefs.RName] =
BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed => ERROR SendFailed[notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state # started THEN ERROR WrongCallSequence[];
ProtocolDefs.SendMSOperation[handle.str, addRecipient];
ProtocolDefs.SendRName[handle.str, recipient];
END;
CheckValidity: PUBLIC ENTRY PROC[handle: Handle,
notify: PROC[CARDINAL, BodyDefs.RName] ]
RETURNS[ ok: CARDINAL ] =
BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed => ERROR SendFailed[notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state # started THEN ERROR WrongCallSequence[];
ProtocolDefs.SendMSOperation[handle.str, checkValidity];
ProtocolDefs.SendNow[handle.str];
DO n: CARDINAL = ProtocolDefs.ReceiveCount[handle.str];
bad: BodyDefs.RName = [BodyDefs.maxRNameLength];
IF n = 0 THEN EXIT;
ProtocolDefs.ReceiveRName[handle.str, bad];
notify[n, bad];
ENDLOOP;
ok ← ProtocolDefs.ReceiveCount[handle.str];
handle.state ← noItem;
END;
StartItem: PUBLIC ENTRY PROC[handle: Handle, type: BodyDefs.ItemType] =
BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed => ERROR SendFailed[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[];
ProtocolDefs.SendMSOperation[handle.str, startItem];
ProtocolDefs.SendCount[handle.str, LOOPHOLE[type]];
handle.state ← inItem;
END;
AddToItem: PUBLIC ENTRY PROC[handle: Handle,
buffer: DESCRIPTOR FOR PACKED ARRAY OF CHARACTER ] =
BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed, PupStream.StreamClosing =>
ERROR SendFailed[notDelivered:TRUE];
UNWIND => Close[handle];
END;
IF handle.state # inItem THEN ERROR WrongCallSequence[];
ProtocolDefs.SendMSOperation[handle.str, addToItem];
ProtocolDefs.SendCount[handle.str, LENGTH[buffer]];
Stream.PutBlock[handle.str,
[ BASE[buffer], 0, LENGTH[buffer] ],
FALSE];
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 --
ProtocolDefs.SendNow[handle.str !
ProtocolDefs.Failed => ERROR SendFailed[notDelivered:TRUE] ];
BEGIN
ENABLE ProtocolDefs.Failed => ERROR SendFailed[notDelivered:FALSE];
ProtocolDefs.SendMSOperation[handle.str, send];
ProtocolDefs.SendNow[handle.str];
ProtocolDefs.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 ProtocolDefs.DestroyStream[handle.str];
handle.str ← NIL;
handle.state ← idle;
END;
ExpandFailed: PUBLIC ERROR = CODE;
Expand: PUBLIC PROC[name: BodyDefs.RName, work: PROC[BodyDefs.RName]]
RETURNS[info: SendDefs.ExpandInfo] =
BEGIN
str: ProtocolDefs.Handle = GetStream[];
IF str = NIL
THEN info ← allDown
ELSE BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed => ERROR ExpandFailed[];
UNWIND => ProtocolDefs.DestroyStream[str];
END;
ProtocolDefs.SendMSOperation[str, expand];
ProtocolDefs.SendRName[str, name];
ProtocolDefs.SendNow[str];
WHILE ProtocolDefs.ReceiveBoolean[str]
DO n: BodyDefs.RName = [BodyDefs.maxRNameLength];
ProtocolDefs.ReceiveRName[str, n];
work[n];
ENDLOOP;
info ← LOOPHOLE[ProtocolDefs.ReceiveByte[str]];
ProtocolDefs.DestroyStream[str];
END;
END;
END.