DIRECTORY GVBasics USING [Password], GVNames USING [GetConnect, IsMemberDirect, Membership, Outcome, SetConnect], GVProtocol USING [Failed, GetSocket, GVSocket, ReceiveRName, ReceiveTimestamp, SendAck, SendNow], IO USING [Close, EndOfStream, PeekChar, STREAM], Process USING [Abort, Detach], Pup USING [allHosts, Address], PupBuffer USING [Buffer], PupName USING [HisName, MyName], PupSocket USING [CreateServer, Destroy, FreeBuffer, Get, ReturnToSender, Socket, waitForever], PupStream USING [ConsumeMark, CreateListener, DestroyListener, Listener, StreamClosing, Timeout], Rope USING [Cat, Equal, ROPE], SMTPControl USING [gvMSName, LoggedInUser, OKToAcceptGVInput], SMTPDescr USING [Create, CreateFailed, Descr, Unparse], SMTPGVRcvr USING [], SMTPSupport USING [Log, LogPriority, Now], SMTPQueue USING [AddNewMessage, StartNewMessage]; SMTPGVRcvrImpl: CEDAR PROGRAM IMPORTS GVNames, GVProtocol, IO, Process, PupName, PupSocket, PupStream, Rope, SMTPControl, SMTPDescr, SMTPSupport, SMTPQueue EXPORTS SMTPGVRcvr = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; Filter: PROC [clientData: REF ANY, remote: Pup.Address] RETURNS [reject: ROPE _ NIL] = { ok: BOOL; whyNot: ROPE; fromName: ROPE = PupName.HisName[remote]; [ok, whyNot] _ SMTPControl.OKToAcceptGVInput[]; IF NOT ok THEN { SMTPSupport.Log[important, "Grapevine input request from ", fromName, " rejected: ", whyNot, "."]; RETURN[whyNot]; } ELSE SMTPSupport.Log[verbose, "Forking AcceptInput process for Grapevine request from ", fromName, "."]; }; AcceptInput: PROC [stream: STREAM, clientData: REF ANY, remote: Pup.Address] = { failureText: ROPE; BEGIN ENABLE { GVProtocol.Failed => { failureText _ Rope.Cat["GVProtocol.Failed: ", text]; GOTO GVMSFailure; }; PupStream.StreamClosing => { failureText _ Rope.Cat["PupStream.StreamClosing: ", text]; GOTO GVMSFailure; }; PupStream.Timeout => { failureText _ "PupStream.Timeout"; GOTO GVMSFailure; }; IO.EndOfStream => { failureText _ "IO.EndOfStream"; GOTO GVMSFailure; }; SMTPDescr.CreateFailed => { failureText _ "SMTPDescr.CreateFailed"; GOTO GVMSFailure; }; }; rawRecipients: LIST OF ROPE _ NIL; descr: SMTPDescr.Descr; timeStampLine: ROPE; fromName: ROPE = GVProtocol.ReceiveRName[stream]; membership: GVNames.Membership = GVNames.IsMemberDirect["*.MS", fromName]; IF membership # yes AND membership # allDown THEN { failureText _ "input request from nonGV mail server"; GOTO GVMSFailure; }; SMTPQueue.StartNewMessage[fromName]; [] _ GVProtocol.ReceiveTimestamp[stream]; DO [] _ stream.PeekChar[ ! IO.EndOfStream => EXIT]; -- needed because ReceiveRName interprets EOF as protocol error and provides no way to distinguish that case from other protocol errors rawRecipients _ CONS[GVProtocol.ReceiveRName[stream], rawRecipients]; ENDLOOP; [] _ PupStream.ConsumeMark[stream]; -- consumes through mark timeStampLine _ Rope.Cat["Received: from ", fromName, " by ", SMTPControl.gvMSName, " ;"]; timeStampLine _ Rope.Cat[timeStampLine, " ", SMTPSupport.Now[].rope]; descr _ SMTPDescr.Create[ rawRecipients: rawRecipients, format: gv, precedeMsgText: timeStampLine, msgStream: stream]; [] _ PupStream.ConsumeMark[stream]; -- consume mark at the end of message body BEGIN ENABLE GVProtocol.Failed => { SMTPSupport.Log[important, "Late GVProtocol.Failed from GV server ", PupName.HisName[remote], "."]; CONTINUE; }; GVProtocol.SendAck[stream]; -- all is on disk GVProtocol.SendNow[stream]; END; SMTPQueue.AddNewMessage[descr, fromName]; SMTPSupport.Log[ noteworthy, SMTPDescr.Unparse[descr], " accepted from ", PupName.HisName[remote], "."]; EXITS GVMSFailure => { SMTPSupport.Log[important, "Failed to accept input from GV server ", PupName.HisName[remote], ",\n", failureText, "."]; } END; stream.Close[]; }; AcceptPoll: PROC ~ TRUSTED { DO b: PupBuffer.Buffer = PupSocket.Get[pollSocket]; IF b.type = echoMe AND b.dest.host # Pup.allHosts THEN { source: Pup.Address _ b.source; fromName: ROPE; b.type _ iAmEcho; PupSocket.ReturnToSender[b]; fromName _ PupName.HisName[source]; SMTPSupport.Log[verbose, "Grapevine poll listener responded to \"echoMe\" request from ", fromName, "."]; LOOP; }; PupSocket.FreeBuffer[b]; ENDLOOP; }; pollSocket: PupSocket.Socket _ NIL; inputListener: PupStream.Listener _ NIL; pollListener: PROCESS; -- only want one of them, pup listener fires new process for each input listenersInOperation: BOOL _ FALSE; SetConnectFailed: ERROR [why: GVNames.Outcome] = CODE; Error: PUBLIC ERROR [reason: ROPE] = CODE; Initialize: PUBLIC PROC = { ENABLE SetConnectFailed => { text: ROPE = SELECT why FROM notFound => "notFound", protocolError => "protocolError", badPwd => "badPwd", outOfDate => "outOfDate", notAllowed => "insufficient authorization", ENDCASE => "???"; reason: ROPE = Rope.Cat[ "Grapevine wouldn't establish connection between ", SMTPControl.gvMSName, " and this machine: ", text, "."]; ERROR Error[reason]; }; me: ROPE = PupName.MyName[]; currentConnectSite: ROPE; outcome: GVNames.Outcome; IF listenersInOperation THEN ERROR Error["SMTP Grapevine listeners already in operation."]; [outcome, currentConnectSite] _ GVNames.GetConnect[SMTPControl.gvMSName]; IF outcome # individual AND outcome # allDown THEN ERROR SetConnectFailed[outcome]; IF ~Rope.Equal[currentConnectSite, me] THEN { -- Fix it up userName: ROPE; userPassword: GVBasics.Password; [userName, userPassword] _ SMTPControl.LoggedInUser[]; SMTPSupport.Log[important, "Moving connect site for ", SMTPControl.gvMSName, " to ", me, "."]; outcome _ GVNames.SetConnect[user: userName, password: userPassword, individual: SMTPControl.gvMSName, connect: me]; IF outcome # noChange AND outcome # group AND outcome # individual AND outcome # allDown THEN ERROR SetConnectFailed[outcome]; }; inputListener _ PupStream.CreateListener[ local: GVProtocol.GetSocket[MSForward], worker: AcceptInput, getTimeout: 60000, putTimeout: 180000, filter: Filter]; pollSocket _ PupSocket.CreateServer[local: GVProtocol.GetSocket[MSPoll], getTimeout: PupSocket.waitForever]; pollListener _ FORK AcceptPoll[]; TRUSTED {Process.Detach[pollListener]}; SMTPSupport.Log[important, "Grapevine Pup Byte Stream listeners started."]; listenersInOperation _ TRUE; }; Finalize: PUBLIC PROC = { IF NOT listenersInOperation THEN ERROR Error["No SMTP Grapevine listeners in operation."]; PupStream.DestroyListener[inputListener]; TRUSTED {Process.Abort[pollListener]}; PupSocket.Destroy[pollSocket]; SMTPSupport.Log[important, "Grapevine Pup Byte Stream listeners destroyed."]; listenersInOperation _ FALSE; }; END. ΎSMTPGVRcvrImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Hal Murray May 20, 1985 1:53:50 am PDT Last Edited by: HGM, July 9, 1984 3:43:33 pm PDT Last Edited by: DCraft, December 14, 1983 5:21 pm Last Edited by: Taft, February 4, 1984 5:06:57 pm PST John Larson, June 4, 1986 11:46:30 pm PDT The file on the disk will be corrupt if the connection gets closed while sloshing the bits. Fixing that isn't trivial, so I haven't done it yet. The corresponding fix for the ARPA side is implemented because a null message actually happened once. corresponding Grapevine module in /indigo/grapevine/ms/receiveinput.mesa Read the from-server-R-Name and ensure it is a Grapevine mailserver. Swallow the timestamp [for now]. Read the recipients (i.e. the RNames until the mark). Create a descriptor for the item, reading the message onto a file. Queue the item for processing and acknowledge. IF b = NIL THEN LOOP; -- I saw it happen. HGM ΚΑ– "cedar" style˜headšœ™Icodešœ Οmœ1™<™&Jšœ0™0Jšœ1™1Jšœ5™5—L™)Ibody™φcode2šΟk ˜ Nšœ žœ ˜Nšœžœ?˜LNšœ žœQ˜aNšžœžœ žœ˜0Nšœžœ˜Nšœžœ˜Nšœ žœ ˜Nšœžœ˜ Nšœ žœO˜^Nšœ žœR˜aNšœžœžœ˜N˜Nšœ žœ-˜>Nšœ žœ(˜7Nšœ žœ˜Nšœ žœ˜*Nšœ žœ"˜1——šΟlœžœž˜šž˜Nšœžœ/˜FNšœ.˜.—Nšžœ ˜Nšž˜Nšžœžœžœžœ˜Nšžœžœžœ˜šΟnœžœžœžœžœ žœžœ˜XNšœžœ žœ˜Nšœ žœ˜)N˜/šžœžœžœ˜Nšœc˜cNšžœ ˜Nšœ˜—šžœ˜N˜d—N˜—š   œžœ žœžœžœ˜PNšœH™HNšœ žœ˜šž˜šžœ˜šœ˜Nšœ4˜4Nšžœ ˜N˜—šœ˜Nšœ:˜:Nšžœ ˜N˜—šœ˜Nšœ"˜"Nšžœ ˜N˜—šžœ˜Nšœ˜Nšžœ ˜N˜—˜N˜'Nšžœ ˜N˜—N˜—Nš œžœžœžœžœ˜"Nšœ˜N˜N™eNšœžœ˜Nšœ žœ#˜1NšœJ˜Jšžœžœžœ˜3Nšœ5˜5Nšžœ ˜N˜N˜—Nšœ$˜$Nšœ)˜)N™N™5šž˜NšœžœžœΟc‡˜ΈNšœžœ1˜ENšžœ˜—Nšœ$‘˜