-- 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.