-- Copyright (C) 1981, 1984, 1985 by Xerox Corporation. All rights reserved.
-- SendMail.mesa: User: sending mail
-- HGM, 15-Sep-85 5:48:33
-- Mark Johnson 10-Dec-81 10:33:17
-- Andrew Birrell 30-Mar-81 13:44:59
DIRECTORY
BodyDefs USING [ItemType, maxRNameLength, Password, RName],
Heap USING [systemZone],
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],
Stream USING [Handle, PutBlock];
SendMail: MONITOR
IMPORTS Heap, LocateDefs, ProtocolDefs, PupStream, Stream EXPORTS SendDefs =
BEGIN
Handle: PUBLIC TYPE = LONG POINTER TO HandleObject;
HandleObject: TYPE = RECORD [
state: {idle, started, noItem, inItem}, str: ProtocolDefs.Handle];
Create: PUBLIC PROC RETURNS [handle: Handle] =
BEGIN
handle ← Heap.systemZone.NEW[HandleObject];
handle↑ ← [state: idle, str: NIL];
END;
Destroy: PUBLIC ENTRY PROC [handle: Handle] = {
Close[handle]; Heap.systemZone.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: LONG 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: LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] =
BEGIN
ENABLE
BEGIN
ProtocolDefs.Failed, PupStream.StreamClosing =>
ERROR SendFailed[notDelivered: TRUE];
UNWIND => Close[handle];
END;
bufferAddr: LONG POINTER = BASE[buffer];
IF handle.state # inItem THEN ERROR WrongCallSequence[];
ProtocolDefs.SendMSOperation[handle.str, addToItem];
ProtocolDefs.SendCount[handle.str, LENGTH[buffer]];
Stream.PutBlock[handle.str, [bufferAddr, 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.