<> <> <> <> <> 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], PupDefs USING [PupAddress], Rope USING [Length, ROPE]; GVSendImpl: CEDAR MONITOR IMPORTS GVBasics, GVLocate, GVProtocol, Rope EXPORTS GVSend = 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; <> mailDrop: Rope.ROPE _ "MailDrop.MS"; <> serverKnown: BOOLEAN _ FALSE; serverAddr: PupDefs.PupAddress; SetMailDropList: PUBLIC PROC [list: Rope.ROPE] = BEGIN mailDrop _ list; serverKnown _ FALSE; 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[]; <> 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.