ArpaSMTPGVRcvrImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by: DCraft, December 14, 1983 5:21 pm
Last Edited by: Taft, February 4, 1984 5:06:57 pm PST
Hal Murray May 20, 1985 1:53:50 am PDT
John Larson, October 11, 1987 5:21:13 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.
DIRECTORY
ArpaConfig USING [gvMSName],
ArpaSMTPControl USING [LoggedInUser, OKToAcceptGVInput],
ArpaSMTPDescr USING [Create, CreateFailed, Descr, Unparse],
ArpaSMTPGVRcvr USING [],
ArpaSMTPSupport USING [Log, LogPriority, Now],
ArpaSMTPQueue USING [AddNewMessage, StartNewMessage],
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];
ArpaSMTPGVRcvrImpl: CEDAR PROGRAM
IMPORTS
ArpaConfig, ArpaSMTPControl, ArpaSMTPDescr, ArpaSMTPSupport, ArpaSMTPQueue, GVNames, GVProtocol, IO, Process, PupName, PupSocket, PupStream, Rope
EXPORTS ArpaSMTPGVRcvr =
BEGIN
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
Filter: PROC [clientData: REF ANY, remote: Pup.Address] RETURNS [reject: ROPENIL] = {
ok: BOOL; whyNot: ROPE;
fromName: ROPE = PupName.HisName[remote];
[ok, whyNot] ← ArpaSMTPControl.OKToAcceptGVInput[];
IF NOT ok THEN {
ArpaSMTPSupport.Log[important,
"Grapevine input request from ", fromName, " rejected: ", whyNot, "."];
RETURN[whyNot];
}
ELSE
ArpaSMTPSupport.Log[verbose,
"Forking AcceptInput process for Grapevine request from ", fromName, "."];
};
AcceptInput: PROC [stream: STREAM, clientData: REF ANY, remote: Pup.Address] = {
corresponding Grapevine module in /indigo/grapevine/ms/receiveinput.mesa
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;
};
ArpaSMTPDescr.CreateFailed => {
failureText ← "ArpaSMTPDescr.CreateFailed";
GOTO GVMSFailure;
};
};
rawRecipients: LIST OF ROPENIL;
descr: ArpaSMTPDescr.Descr;
Read the from-server-R-Name and ensure it is a Grapevine mailserver. Swallow the timestamp [for now].
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;
};
ArpaSMTPQueue.StartNewMessage[fromName];
[] ← GVProtocol.ReceiveTimestamp[stream];
Read the recipients (i.e. the RNames until the mark).
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
Create a descriptor for the item, reading the message onto a file.
timeStampLine ← Rope.Cat["Received: from ", fromName, " by ", ArpaConfig.gvMSName, " ;"];
timeStampLine ← Rope.Cat[timeStampLine, " ", ArpaSMTPSupport.Now[].rope];
descr ← ArpaSMTPDescr.Create[
rawRecipients: rawRecipients,
format: gv,
precedeMsgText: timeStampLine,
msgStream: stream];
Queue the item for processing and acknowledge.
[] ← PupStream.ConsumeMark[stream]; -- consume mark at the end of message body
BEGIN ENABLE GVProtocol.Failed => {
ArpaSMTPSupport.Log[important,
"Late GVProtocol.Failed from GV server ",
PupName.HisName[remote], "."];
CONTINUE; };
GVProtocol.SendAck[stream]; -- all is on disk
GVProtocol.SendNow[stream];
END;
ArpaSMTPQueue.AddNewMessage[descr, fromName];
ArpaSMTPSupport.Log[
noteworthy,
ArpaSMTPDescr.Unparse[descr], " accepted from ",
PupName.HisName[remote], "."];
EXITS
GVMSFailure => {
ArpaSMTPSupport.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 = NIL THEN LOOP; -- I saw it happen. HGM
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];
ArpaSMTPSupport.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: BOOLFALSE;
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 ",
ArpaConfig.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[ArpaConfig.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] ← ArpaSMTPControl.LoggedInUser[];
ArpaSMTPSupport.Log[important,
"Moving connect site for ", ArpaConfig.gvMSName, " to ", me, "."];
outcome ← GVNames.SetConnect[user: userName,
password: userPassword,
individual: ArpaConfig.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]};
ArpaSMTPSupport.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];
ArpaSMTPSupport.Log[important,
"Grapevine Pup Byte Stream listeners destroyed."];
listenersInOperation ← FALSE; };
END.