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