DIRECTORY BasicTime USING [GMT, MonthOfYear, Now, nullGMT, Unpack, Unpacked], Convert USING [RopeFromInt, RopeFromTime], IO USING [BreakProc, Close, CreateStream, CreateStreamProcs, EndOfStream, Error, Flush, GetBlock, GetChar, GetIndex, GetInfo, GetLength, GetTokenRope, int, PutChar, PutFR, PutRope, RIS, rope, RopeFromROS, ROS, SetIndex, STREAM, StreamProcs], GVNames USING [IsMemberDirect, Membership], Process USING [Detach], RefText USING [AppendChar, ObtainScratch, ReleaseScratch], Rope USING [Cat, Concat, Equal, Fetch, Find, FromRefText, IsEmpty, Length, ROPE, Substr], ViewerIO USING [CreateViewerStreams, GetViewerFromStream], MT USING [CheckFromField, Info, ParseHeaders, PrintHeaders, TranslateToArpa, TranslateToGrapevine], SMTPControl USING [arpaExceptions, deadLetterName, deadLetterSenderName, defaultLogAcceptPriority, longGVMSName, notifyManagerNames], SMTPDescr USING [CopyForReturn, Create, CreateFailed, Descr, EnumerateRawRecipients, GetArpaReversePath, GetFormat, GetGvSender, GetPrecedeMsgText, GetReturnPathLine, RawRecipProc, RetrieveMsgStream, Unparse, WrongState], SMTPQueue USING [AddNewMessage], SMTPSupport USING [LogPriority], SMTPSyntax USING [EnumerateGVItems, GVItemProc]; SMTPSupportImpl: CEDAR MONITOR IMPORTS BasicTime, Convert, IO, GVNames, Process, RefText, Rope, ViewerIO, MT, SMTPControl, SMTPDescr, SMTPQueue, SMTPSyntax EXPORTS SMTPSupport = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Descr: TYPE = SMTPDescr.Descr; currentLogAcceptPriority: PUBLIC SMTPSupport.LogPriority _ SMTPControl.defaultLogAcceptPriority; Log: PUBLIC PROC [priority: SMTPSupport.LogPriority, note1, note2, note3, note4, note5, note6, note7, note8, note9, note10: Rope.ROPE _ NIL] = { now: BasicTime.GMT _ BasicTime.Now[]; IF priority >= currentLogAcceptPriority THEN WriteToLog[priority, now, note1, note2, note3, note4, note5, note6, note7, note8, note9, note10]; IF priority >= ATTENTION THEN { -- notify managers ENABLE SMTPDescr.CreateFailed => CONTINUE; date: ROPE _ Rope.Cat["Date: ", RFC822Date[now], "\n"]; from: ROPE _ Rope.Cat["From: ", SMTPControl.deadLetterSenderName, "\n"]; to: ROPE _ Rope.Cat["To: ", SMTPControl.notifyManagerNames.first, "\n"]; subject: ROPE _ "Subject: Confusion in Mail Gateway\n\n"; header: ROPE _ Rope.Cat[date, from, to, subject]; descr: Descr _ SMTPDescr.Create[ gvSender: SMTPControl.deadLetterSenderName, rawRecipients: SMTPControl.notifyManagerNames, format: arpa, -- don't have GV items msgStream: IO.RIS[Rope.Cat[header, Rope.Cat[note1, note2, note3, note4, note5], Rope.Cat[note6, note7, note8, note9, note10]]]]; ForkNewMessage[descr, "ATTENTION"]; }; }; ForkNewMessage: PROC [descr: Descr, queue: ROPE] = TRUSTED { Process.Detach[FORK SMTPQueue.AddNewMessage[descr, queue]]; }; HeaderParseError: PUBLIC PROC [recipList: LIST OF ROPE, descr: Descr] = { ENABLE SMTPDescr.CreateFailed => CONTINUE; now: BasicTime.GMT _ BasicTime.Now[]; date: ROPE _ Rope.Cat["Date: ", RFC822Date[now], "\n"]; from: ROPE _ Rope.Cat["From: ", SMTPControl.deadLetterSenderName, "\n"]; to: ROPE _ Rope.Cat["To: ", SMTPControl.arpaExceptions.first, "\n"]; subject: ROPE _ "Subject: Parsing Error in Message Header\n\n"; header: ROPE _ Rope.Cat[date, from, to, subject]; getReturnPathLine: ROPE _ SMTPDescr.GetReturnPathLine[descr]; getPrecedeMsgText: ROPE _ SMTPDescr.GetPrecedeMsgText[descr]; msgStream: IO.STREAM; errors: IO.STREAM _ IO.ROS[]; before: IO.STREAM _ IO.ROS[]; after: IO.STREAM _ IO.ROS[]; info: MT.Info; body: ROPE; new: Descr; msgStream _ SMTPDescr.RetrieveMsgStream[descr]; SELECT SMTPDescr.GetFormat[descr] FROM arpa => { info _ MT.ParseHeaders[file: msgStream, errStream: errors]; msgStream.Close[]; MT.PrintHeaders[info, before]; MT.TranslateToGrapevine[info]; MT.PrintHeaders[info, after]; }; gv => { -- Copied from SMTPSendImpl AssignTextStream: SMTPSyntax.GVItemProc = { currentIndex: INT; IF itemHeader.type # Text THEN RETURN; currentIndex _ msgStream.GetIndex[]; msgStream _ CreateSubrangeStream[ origStream: msgStream, min: currentIndex, max: currentIndex+itemHeader.length]; continue _ FALSE; }; SMTPSyntax.EnumerateGVItems[GVStream: msgStream, proc: AssignTextStream]; info _ MT.ParseHeaders[file: msgStream, errStream: errors]; msgStream.Close[]; MT.PrintHeaders[info, before]; MT.TranslateToArpa[info]; MT.PrintHeaders[info, after]; }; ENDCASE => ERROR; body _ Rope.Cat[body, "Recipients: "]; FOR rest: LIST OF ROPE _ recipList, rest.rest UNTIL rest = NIL DO IF rest # recipList THEN body _ Rope.Cat[body, ", "]; body _ Rope.Cat[body, rest.first]; ENDLOOP; body _ Rope.Cat[body, "\n\n"]; body _ Rope.Cat[body, IO.RopeFromROS[errors], "\n"]; body _ Rope.Cat[body, "Before translation:\n\n", IO.RopeFromROS[before], "\n\n"]; body _ Rope.Cat[body, "After translation:\n\n"]; IF getReturnPathLine # NIL THEN body _ Rope.Cat[body, getReturnPathLine, "\n"]; IF getPrecedeMsgText # NIL THEN body _ Rope.Cat[body, getPrecedeMsgText, "\n"]; body _ Rope.Cat[body, IO.RopeFromROS[after], "\n\n"]; new _ SMTPDescr.Create[ gvSender: SMTPControl.deadLetterSenderName, rawRecipients: SMTPControl.arpaExceptions, format: arpa, -- don't have GV items msgStream: IO.RIS[Rope.Cat[header, body]]]; ForkNewMessage[new, "HeaderParseError"]; }; Undeliverable: PROC [why: ROPE, descr: Descr] = { ENABLE SMTPDescr.CreateFailed => CONTINUE; now: BasicTime.GMT _ BasicTime.Now[]; date: ROPE _ Rope.Cat["Date: ", RFC822Date[now], "\n"]; from: ROPE _ Rope.Cat["From: ", SMTPControl.deadLetterSenderName, "\n"]; to: ROPE _ Rope.Cat["To: ", SMTPControl.deadLetterName, "\n"]; subject: ROPE _ "Subject: Undeliverable mail notification\n\n"; header: ROPE _ Rope.Cat[date, from, to, subject]; getReturnPathLine: ROPE _ SMTPDescr.GetReturnPathLine[descr]; getPrecedeMsgText: ROPE _ SMTPDescr.GetPrecedeMsgText[descr]; msgStream: IO.STREAM; errors: IO.STREAM _ IO.ROS[]; before: IO.STREAM _ IO.ROS[]; info: MT.Info; sender, body: ROPE; new: Descr; msgStream _ SMTPDescr.RetrieveMsgStream[descr]; SELECT SMTPDescr.GetFormat[descr] FROM arpa => { sender _ SMTPDescr.GetArpaReversePath[descr]; info _ MT.ParseHeaders[file: msgStream, errStream: errors]; msgStream.Close[]; MT.PrintHeaders[info, before]; }; gv => { -- Copied from SMTPSendImpl AssignTextStream: SMTPSyntax.GVItemProc = { currentIndex: INT; IF itemHeader.type # Text THEN RETURN; currentIndex _ msgStream.GetIndex[]; msgStream _ CreateSubrangeStream[ origStream: msgStream, min: currentIndex, max: currentIndex+itemHeader.length]; continue _ FALSE; }; sender _ SMTPDescr.GetGvSender[descr]; SMTPSyntax.EnumerateGVItems[GVStream: msgStream, proc: AssignTextStream]; info _ MT.ParseHeaders[file: msgStream, errStream: errors]; msgStream.Close[]; MT.PrintHeaders[info, before]; }; ENDCASE => ERROR; body _ Rope.Cat[why, "\n\nThe message will be sent to:\n", sender, ".\n\n"]; body _ Rope.Cat[body, "The header of the message was:\n--------------------\n"]; IF getReturnPathLine # NIL THEN body _ Rope.Cat[body, getReturnPathLine, "\n"]; IF getPrecedeMsgText # NIL THEN body _ Rope.Cat[body, getPrecedeMsgText, "\n"]; body _ Rope.Cat[body, IO.RopeFromROS[before], "\n\n"]; new _ SMTPDescr.Create[ gvSender: SMTPControl.deadLetterSenderName, rawRecipients: LIST[SMTPControl.deadLetterName], format: arpa, -- don't have GV items msgStream: IO.RIS[Rope.Cat[header, body]]]; ForkNewMessage[new, "Undeliverable"]; }; WriteToLog: ENTRY PROC [priority: SMTPSupport.LogPriority, now: BasicTime.GMT, note1, note2, note3, note4, note5, note6, note7, note8, note9, note10: Rope.ROPE _ NIL] = { logViewerOut.PutRope[Convert.RopeFromTime[from: now, start: hours, end: seconds, useAMPM: FALSE, includeZone: FALSE]]; logViewerOut.PutRope[" "]; IF priority > noteworthy THEN { logViewerOut.PutRope[logPriorityNames[priority]]; logViewerOut.PutRope[": "]; }; logViewerOut.PutRope[note1]; logViewerOut.PutRope[note2]; logViewerOut.PutRope[note3]; logViewerOut.PutRope[note4]; logViewerOut.PutRope[note5]; logViewerOut.PutRope[note6]; logViewerOut.PutRope[note7]; logViewerOut.PutRope[note8]; logViewerOut.PutRope[note9]; logViewerOut.PutRope[note10]; logViewerOut.PutChar['\n]; logViewerOut.Flush[]; }; logPriorityNames: PUBLIC ARRAY SMTPSupport.LogPriority OF ROPE _ ["verbose", "noteworthy", "Important", "ATTENTION", "CRITICAL"]; logViewerIn, logViewerOut: STREAM; -- initialized below letNonMembersPlay: BOOL _ TRUE; goodGuys, badGuys: LIST OF ROPE; AuthorizationCheck: PUBLIC PROC [sender: ROPE] RETURNS [ok: BOOLEAN] = { RETURN[TRUE]}; StripQuotes: PROC [old: ROPE] RETURNS [new: ROPE] = { length: INT _ Rope.Length[old]; new _ old; IF length < 2 THEN RETURN; IF Rope.Fetch[old, 0] # '\" THEN RETURN; BEGIN quoteSeen: BOOLEAN _ FALSE; text: REF TEXT _ RefText.ObtainScratch[length]; FOR i: INT IN [0..length) DO c: CHAR = Rope.Fetch[new, i]; IF c = '\\ AND ~quoteSeen THEN { quoteSeen _ TRUE; LOOP; }; quoteSeen _ FALSE; IF c = '\" THEN LOOP; -- Not quite right, but works for reasonable input text _ RefText.AppendChar[text, c]; ENDLOOP; new _ Rope.FromRefText[text]; RefText.ReleaseScratch[text]; END; }; CheckHeader: PUBLIC PROC [sender: ROPE, descr: Descr] RETURNS [ok: BOOLEAN _ TRUE] = { msgStream: IO.STREAM; errors: IO.STREAM _ IO.ROS[]; precede: ROPE _ SMTPDescr.GetPrecedeMsgText[descr]; errorRope: ROPE; info: MT.Info; IF Rope.Find[precede, "\n\n"] #-1 THEN RETURN; SELECT SMTPDescr.GetFormat[descr] FROM arpa => RETURN; -- Don't check ARPA headers (yet) gv => { -- Copied from SMTPSendImpl AssignTextStream: SMTPSyntax.GVItemProc = { currentIndex: INT; IF itemHeader.type # Text THEN RETURN; currentIndex _ msgStream.GetIndex[]; msgStream _ CreateSubrangeStream[ origStream: msgStream, min: currentIndex, max: currentIndex+itemHeader.length]; continue _ FALSE; }; ArpaSender: PROC[descr: Descr] RETURNS[BOOLEAN] = { arpaReversePath: Rope.ROPE _ SMTPDescr.GetArpaReversePath[descr]; IF Rope.Find[SMTPDescr.GetGvSender[descr], "@"] > -1 THEN RETURN[TRUE]; IF Rope.Find[s1: arpaReversePath, s2: "Owners-"] > -1 THEN RETURN[TRUE]; RETURN[Rope.Find[arpaReversePath, "@"] > -1 AND Rope.Find[s1: arpaReversePath, s2: "@Xerox", case: FALSE] = -1]; }; msgStream _ SMTPDescr.RetrieveMsgStream[descr]; SMTPSyntax.EnumerateGVItems[GVStream: msgStream, proc: AssignTextStream]; info _ MT.ParseHeaders[file: msgStream, errStream: errors]; msgStream.Close[]; errorRope _ IO.RopeFromROS[errors]; IF Rope.IsEmpty[errorRope] AND ~ArpaSender[descr] AND ~MT.CheckFromField[info] THEN errorRope _ "A weird character in the From field (even if properly quoted) kills some mailers. If your address (shown below) ends with a \".ns\", check with CINSupport:All Areas to see if your domain should be registered with the Grapevine mail gateways."; IF ~Rope.IsEmpty[errorRope] AND ~ArpaSender[descr] THEN { recipients: ROPE; CopyRawRecipient: SMTPDescr.RawRecipProc = { IF recipients = NIL THEN recipients _ "Recipients: " ELSE recipients _ Rope.Cat[recipients, ", "]; recipients _ Rope.Cat[recipients, rawRecipient]; }; SMTPDescr.EnumerateRawRecipients[descr, CopyRawRecipient, NIL]; errorRope _ Rope.Cat["The Arpa Mail Gateway encountered troubles in the header of your message. In order to avoid confusing other sites your message was not delivered to any remote recipients.\n\n", recipients, "\n\n", errorRope]; NotifySender[descr, errorRope]; ok _ FALSE; }; }; ENDCASE => ERROR; }; NotifySender: PUBLIC PROC [descr: Descr, why1, why2, why3, why4, why5: ROPE _ NIL] = { ENABLE { SMTPDescr.CreateFailed => Log[ATTENTION, "Unable to create descriptor to return item ", SMTPDescr.Unparse[descr], " to sender. Being returned because: \"", why1, why2, why3, why4, why5, "\".\nToo bad!"]; SMTPDescr.WrongState => Log[noteworthy, "Asked to return item ", SMTPDescr.Unparse[descr], " to sender, because: \"", why1, why2, why3, why4, why5, "\"\nThe item has a null reverse path (possibly a return message itself). Too bad!"]; }; why: ROPE = Rope.Cat[why1, why2, why3, why4, why5]; new: Descr; Undeliverable[why, descr]; new _ SMTPDescr.CopyForReturn[descr, Rope.Cat[why, "\n\nThe text of your message was\n--------------------\n"]]; Log[important, SMTPDescr.Unparse[descr], " is being returned because:\n", why1, why2, why3, why4, why5]; ForkNewMessage[new, "ReturnToSender"]; }; monthText: ARRAY BasicTime.MonthOfYear OF ROPE = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", "???"]; USzones: TYPE ~ [4..10]; zones1stChar: ARRAY USzones OF ROPE = ["A", "E", "C", "M", "P", "Y", "H"]; Now: PUBLIC PROC [compressed: BOOL _ FALSE] RETURNS [rope: ROPE, gmt: BasicTime.GMT] = { unpacked: BasicTime.Unpacked; date, time: ROPE; z: INT; unpacked _ BasicTime.Unpack[gmt _ BasicTime.Now[]]; IF compressed THEN { date _ IO.PutFR["%02G%02G%02G-", IO.int[unpacked.year MOD 100], IO.int[unpacked.month.ORD+1], IO.int[unpacked.day]]; time _ IO.PutFR["%02g%02g%02g", IO.int[unpacked.hour], IO.int[unpacked.minute], IO.int[unpacked.second]]; } ELSE { date _ IO.PutFR["%02g %g %02g ", IO.int[unpacked.day], IO.rope[monthText[unpacked.month]], IO.int[unpacked.year MOD 100]]; time _ IO.PutFR["%02g:%02g:%02g ", IO.int[unpacked.hour], IO.int[unpacked.minute], IO.int[unpacked.second]]; z _ unpacked.zone/60; -- gives zone in range -12 to 12 IF (FIRST[USzones] <= z) AND (z <= LAST[USzones]) THEN time _ Rope.Cat[time, zones1stChar[z], IF unpacked.dst = yes THEN "D" ELSE "S", "T"] ELSE { IF unpacked.dst = yes THEN z _ z - 1; IF z = 0 THEN time _ Rope.Concat[time, "GMT"] ELSE time _ Rope.Cat[ time, IF z > 0 THEN "+" ELSE "-", Convert.RopeFromInt[ABS[z]]]; }; }; RETURN[Rope.Concat[date, time], gmt]; }; -- end Now RFC822Date: PUBLIC PROC[gmt: BasicTime.GMT_ BasicTime.nullGMT] RETURNS[date: ROPE] = BEGIN OPEN IO; upt: BasicTime.Unpacked _ BasicTime.Unpack[IF gmt = BasicTime.nullGMT THEN BasicTime.Now[] ELSE gmt]; zone: ROPE; weekday, month, tyme, year: ROPE; timeFormat: ROPE = "%02g:%02g:%02g %g"; -- "hh:mm:ss zzz" dateFormat: ROPE = "%2g %g %g %g"; -- "dd mmm yy timeFormat" arpaNeg: BOOL _ upt.zone > 0; aZone: INT _ ABS[upt.zone]; zDif: INT _ aZone / 60; zMul: INT _ zDif * 60; IF (zMul = aZone) AND arpaNeg THEN { IF upt.dst = yes THEN SELECT zDif FROM 0 => zone_ "UT"; 4 => zone_ "EDT"; 5 => zone_ "CDT"; 6 => zone_ "MDT"; 8 => zone_ "PDT"; ENDCASE ELSE SELECT zDif FROM 0 => zone_ "UT"; 5 => zone_ "EST"; 6 => zone_ "CST"; 7 => zone_ "MST"; 8 => zone_ "PST"; ENDCASE; }; IF zone = NIL THEN { mm: INT_ aZone - zMul; zone_ PutFR[IF arpaNeg THEN "-%02g%02g" ELSE "+%02g%02g", int[zDif], int[mm]]; }; SELECT upt.month FROM January => month_ "Jan"; February => month_ "Feb"; March => month_ "Mar"; April => month_ "Apr"; May => month_ "May"; June => month_ "Jun"; July => month_ "Jul"; August => month_ "Aug"; September => month_ "Sep"; October => month_ "Oct"; November => month_ "Nov"; December => month_ "Dec"; unspecified => ERROR; ENDCASE => ERROR; SELECT upt.weekday FROM Monday => weekday_ "Monday"; Tuesday => weekday_ "Tuesday"; Wednesday => weekday _ "Wednesday"; Thursday => weekday _ "Thursday"; Friday => weekday _ "Friday"; Saturday => weekday _ "Saturday"; Sunday => weekday _ "Sunday"; unspecified => ERROR; ENDCASE => ERROR; year_ Rope.Substr[PutFR[NIL, int[upt.year]], 2]; tyme_ PutFR[timeFormat, int[upt.hour], int[upt.minute], int[upt.second], rope[zone]]; date_ PutFR[dateFormat, int[upt.day], rope[month], rope[year], rope[tyme]]; date _ Rope.Cat[date, " (", weekday, ")"]; END; CreateSubrangeStream: PUBLIC PROC [origStream: STREAM, min, max: INT] RETURNS [STREAM] = { newStreamData: REF SubrangeStreamData; newBackingStream: STREAM; IF origStream.GetInfo[].class = $Subrange THEN { origStreamData: REF SubrangeStreamData = NARROW[origStream.streamData]; origMinIndex: INT = origStreamData.min; origMaxIndex: INT = origStreamData.max; newBackingStream _ origStream.backingStream; newStreamData _ NEW[SubrangeStreamData _ [min: MAX[min, origMinIndex], max: MIN[max, origMaxIndex]]]; } ELSE { newBackingStream _ origStream; newStreamData _ NEW[SubrangeStreamData _ [min: min, max: MIN[max, origStream.GetLength[]]]]; }; RETURN[IO.CreateStream[ backingStream: newBackingStream, streamProcs: SubrangeStreamProcs, streamData: newStreamData]]; }; SubrangeStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[ variety: input, class: $Subrange, getChar: SubrangeGetChar, getBlock: SubrangeGetBlock, endOf: SubrangeEndOf, backup: SubrangeBackup, -- Bug in real one setIndex: SubrangeSetIndex, getLength: SubrangeGetLength]; SubrangeStreamData: TYPE = RECORD[min, max: INT]; -- Info is [min..max) SubrangeBackup: PROC [self: STREAM, char: CHAR] = { me: REF SubrangeStreamData = NARROW[self.streamData]; self.SetIndex[self.GetIndex[]-1]; }; SubrangeEndOf: PROC [self: STREAM] RETURNS [BOOL] = { me: REF SubrangeStreamData = NARROW[self.streamData]; RETURN[self.backingStream.GetIndex[] >= me.max]; }; SubrangeGetLength: PROC [self: STREAM] RETURNS [length: INT] = { me: REF SubrangeStreamData = NARROW[self.streamData]; RETURN[me.max]; }; SubrangeSetIndex: PROC [self: STREAM, index: INT] = { me: REF SubrangeStreamData = NARROW[self.streamData]; IF index > me.max OR index < me.min THEN ERROR IO.Error[BadIndex, self]; self.backingStream.SetIndex[index]; }; SubrangeGetBlock: PROC [self: STREAM, block: REF TEXT, startIndex: NAT _ 0, count: NAT _ NAT.LAST] RETURNS [nBytesRead: NAT] = { me: REF SubrangeStreamData = NARROW[self.streamData]; bytesLeft: INT = me.max - self.backingStream.GetIndex[]; bytesToRead: INT = MIN[count, bytesLeft]; natBytesToRead: NAT = bytesToRead; nBytesRead _ self.backingStream.GetBlock[ block: block, startIndex: startIndex, count: natBytesToRead]; }; SubrangeGetChar: PROC [self: STREAM] RETURNS [CHAR] = { me: REF SubrangeStreamData = NARROW[self.streamData]; fullStream: STREAM ~ self.backingStream; IF fullStream.GetIndex[] >= me.max THEN IO.EndOfStream[self]; RETURN[fullStream.GetChar[]]; }; RopeFromSubrange: PUBLIC PROC [origStream: STREAM, min, max: INT] RETURNS [rope: ROPE] = { EverythingProc: IO.BreakProc = {RETURN[other]}; subrangeStream: STREAM; subrangeStream _ CreateSubrangeStream[origStream: origStream, min: min, max: max]; rope _ subrangeStream.GetTokenRope[EverythingProc ! IO.EndOfStream => CONTINUE ].token; }; [logViewerIn, logViewerOut] _ ViewerIO.CreateViewerStreams[ name: "MailGateway Log", backingFile: "MailGateway.log"]; ViewerIO.GetViewerFromStream[logViewerOut].inhibitDestroy _ TRUE; logViewerOut.PutRope["########## "]; logViewerOut.PutRope[SMTPControl.longGVMSName]; logViewerOut.PutRope[" Log ##########\n\n"]; END.  SMTPSupportImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: DCraft, December 20, 1983 5:54 pm Last Edited by: Taft, January 23, 1984 1:27:58 pm PST Hal Murray June 17, 1985 11:28:06 am PDT John Larson, March 9, 1987 12:26:54 pm PST Logging Information Monitored so we don't interleave messages on the log. Housekeeping There is similar code in SMTPQueueImpl membership: GVNames.Membership; IF Rope.Find[sender, "@"] # -1 THEN RETURN[TRUE]; -- Arpa => GV DL => Arpa sender _ StripQuotes[sender]; FOR list: LIST OF ROPE _ goodGuys, list.rest UNTIL list = NIL DO IF Rope.Equal[sender, list.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; FOR list: LIST OF ROPE _ badGuys, list.rest UNTIL list = NIL DO IF Rope.Equal[sender, list.first, FALSE] THEN RETURN[letNonMembersPlay]; ENDLOOP; membership _ GVNames.IsMemberDirect["ArpanetUsers^.X", sender]; SELECT membership FROM yes => { goodGuys _ CONS[sender, goodGuys]; RETURN[TRUE]; }; no => { Log[important, "\"", sender, "\" isn't a member of ArpanetUsers^.X"]; badGuys _ CONS[sender, badGuys]; RETURN[letNonMembersPlay]; }; notGroup => Log[ATTENTION, "GV Claims ArpanetUsers^.X isn't a group"]; allDown => Log[ATTENTION, "GV Claims all servers for the X registry are down"]; ENDCASE => NULL; RETURN[TRUE]; }; JSmith.OSBUNorth => JSmith.OSBUNorth "John Smith".OSBUNorth => John Smith.OSBUNorth Beware: Returned mail uses the precede hackery. The actual descr has the same body and hence looks like it has the same header so it will hit the same problem each time it gets returned. Notification of Undeliverable Mail Current Date and Time (in SMTP format) Returns the current time in format specified for Arpa SMTP. The "compressed" form is suitable for a file name. generates arpa standard time, dd mmm yy hh:mm:ss zzz Subrange Streams Create a new stream encompassing only a subrange of the original one. If the original stream is already a subrange stream, do not layer again. This assumes that GetChar/GetBlock set the index to the length after reading the last char. Misc Κ&– "cedar" style˜head™Icodešœ Οmœ1™˜HMšœŸœ@˜HMšœ Ÿœ,˜9MšœŸœ%˜1šœ ˜ Mšœ+˜+Mšœ.˜.MšœΠklœ’˜$šœ ŸœŸœ˜"Mšœ,˜,Mšœ0˜0——Mšœ)˜)——š‘œŸœŸœŸœ˜˜>—š ‘œŸœŸœ ŸœŸœŸœ˜IMšŸœŸœ˜*Mšœ%˜%MšœŸœ-˜7MšœŸœ>˜HMšœŸœ<˜DMšœ Ÿœ2˜?MšœŸœ%˜1MšœŸœ&˜=MšœŸœ&˜=Mšœ ŸœŸœ˜MšœŸœŸœ˜MšœŸœŸœ˜MšœŸœŸœ˜MšœŸœ˜MšœŸœ˜ M˜ Mšœ/˜/šŸœŸ˜&š£Ÿœ˜ MšœŸœ2˜;Mšœ˜MšŸœ˜MšŸœ˜MšŸœ˜ —šΟlœ’˜#š‘œ˜+MšœŸœ˜MšŸœŸœŸœ˜&M˜$˜!M˜O—Mšœ Ÿœ˜—MšœI˜IMšœŸœ2˜;Mšœ˜MšŸœ˜MšŸœ˜MšŸœ˜ —MšŸœŸœ˜—Mšœ&˜&š ŸœŸœŸœŸœŸœŸœŸ˜AMšŸœŸœ˜5Mšœ"˜"MšŸœ˜—Mšœ˜MšœŸœ˜4Mšœ1Ÿœ˜QMšœ0˜0MšŸœŸœŸœ0˜OMšŸœŸœŸœ0˜OMšœŸœ˜5šœ˜Mšœ+˜+Mšœ*˜*Mšœ£œ’˜$Mšœ ŸœŸœ˜+—Mšœ+˜+—š‘ œŸœŸœ˜1MšŸœŸœ˜*Mšœ%˜%MšœŸœ-˜7MšœŸœ>˜HMšœŸœ6˜>Mšœ Ÿœ2˜?MšœŸœ%˜1MšœŸœ&˜=MšœŸœ&˜=Mšœ ŸœŸœ˜MšœŸœŸœ˜MšœŸœŸœ˜MšœŸœ˜MšœŸœ˜M˜ Mšœ/˜/šŸœŸ˜&š£Ÿœ˜ Mšœ-˜-MšœŸœ2˜;Mšœ˜MšŸœ˜!—š€œ’˜#š‘œ˜+MšœŸœ˜MšŸœŸœŸœ˜&M˜$˜!M˜O—Mšœ Ÿœ˜—Mšœ&˜&MšœI˜IMšœŸœ2˜;Mšœ˜MšŸœ˜!—MšŸœŸœ˜—MšœL˜LMšœP˜PMšŸœŸœŸœ0˜OMšŸœŸœŸœ0˜OMšœŸœ˜6šœ˜Mšœ+˜+MšœŸœ˜0Mšœ£œ’˜$Mšœ ŸœŸœ˜+—Mšœ(˜(—š‘ œŸ œ†ŸœŸœ˜«Mšœ5™5MšœZŸœŸœ˜vMšœ˜šœ˜Mšœ1˜1Mšœ˜—Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜M˜—Mš œŸœŸœŸœŸœD˜‚MšœŸœ’˜7™ Mšœ&™&MšœŸœŸœ˜MšœŸœŸœŸœ˜ —š ‘œŸœŸœ ŸœŸœŸœ˜HMšŸœŸœ˜M˜Mšœ™Mš ŸœŸœŸœŸœ’™JMšœ™š ŸœŸœŸœŸœŸœŸœŸ™@Mš Ÿœ ŸœŸœŸœŸœ™;MšŸœ™—š ŸœŸœŸœŸœŸœŸœŸ™?MšŸœ ŸœŸœŸœ™HMšŸœ™—Mšœ?™?šŸœ Ÿ™šœŸ™Mšœ Ÿœ™"MšŸœŸœ™—šœ™MšœE™EMšœ Ÿœ™ MšŸœ™—MšœŸ œ-™FMšœŸ œ7™OMšŸœŸœ™—MšŸœŸœ™—š ‘ œŸœŸœŸœŸœ˜5Mšœ$™$Mšœ.™.MšœŸœ˜M˜ MšŸœ ŸœŸœ˜MšŸœŸœŸœ˜(MšŸ˜Mšœ ŸœŸœ˜MšœŸœŸœ!˜/šŸœŸœŸœ Ÿ˜MšœŸœ˜Mš Ÿœ Ÿœ ŸœŸœŸœ˜;Mšœ Ÿ ˜MšŸœ ŸœŸœ’2˜HMšœ#˜#MšŸœ˜—Mšœ˜Mšœ˜MšŸœ˜—š‘ œŸœŸœ ŸœŸœŸœŸœ˜VMšœ ŸœŸœ˜Mš œŸœŸœŸœŸœ˜Mšœ Ÿœ#˜3Mšœ Ÿœ˜MšœŸœ˜MšœΊ™ΊMšŸœ ŸœŸœ˜.šŸœŸ˜&Mš€œŸœ’!˜1š€œ’˜#š‘œ˜+MšœŸœ˜MšŸœŸœŸœ˜&M˜$˜!M˜O—Mšœ Ÿœ˜—š‘ œŸœŸœŸœ˜3MšœŸœ'˜AMšŸœ3ŸœŸœŸœ˜GMšŸœ4ŸœŸœŸœ˜HMšŸœ&Ÿœ6Ÿœ˜rMšœ˜—Mšœ/˜/MšœI˜IMšœŸœ2˜;Mšœ˜Mšœ Ÿœ˜#š ŸœŸœŸœŸœŸ˜SMšœ˜—šŸœŸœŸœ˜9Mšœ Ÿœ˜šœ,˜,MšŸœŸœŸœ˜4MšŸœ)˜-Mšœ3˜3—Mšœ:Ÿœ˜?Mšœζ˜ζMšœ˜MšœŸœ˜——MšŸœŸœ˜——Mšœ"™"š ‘ œŸœŸœ/ŸœŸœ˜WšŸœ˜˜Mšœ΄˜΄—˜MšœΤ˜Τ—M˜—MšœŸœ*˜3Mšœ ˜ Mšœ˜Mšœr˜rMšœj˜jMšœ)˜)—šœΟrΠkr₯™&Mšœ ŸœŸœŸœ_˜Mšœ Ÿœ ˜MšœŸœ ŸœŸœ'˜J—š‘œŸœŸœŸœŸœŸœŸœŸœ˜XMšœn™nMšœ˜Mšœ Ÿœ˜MšœŸœ˜Mšœ3˜3šŸœ Ÿœ˜Mš œŸœŸœŸœŸœŸœ˜wMš œŸœŸœŸœŸœ˜n—šŸœ˜Mš œŸœŸœŸœ#ŸœŸœ˜}Mš œŸœŸœŸœŸœ˜oMšœ’ ˜6š ŸœŸœŸœŸœ Ÿ˜6Mšœ'ŸœŸœŸœ ˜T—šŸœ˜MšŸœŸœ ˜%MšŸœŸœ ˜-šŸœ˜Mš œŸœŸœŸœŸœ ˜E———MšŸœ˜%Mšœ’ ˜ —š ‘ œŸœŸœŸœŸœŸœ˜TMšœ4™4MšŸœŸœŸœ˜Mšœ+ŸœŸ˜JMšœŸœ˜MšœŸœ˜ MšœŸœ˜!Mšœ Ÿœ’˜:Mšœ Ÿœ’œ˜>Mšœ Ÿœ˜MšœŸœŸœ ˜MšœŸœ ˜MšœŸœ ˜šŸœŸœ Ÿœ˜$šŸœŸ˜šŸœŸ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜MšŸ˜——šŸ˜šŸœŸ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜MšŸœ˜ ———šŸœŸœŸœ˜MšœŸœ˜Mšœ Ÿœ Ÿœ Ÿœ%˜QM˜—šŸœ Ÿ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜MšœŸœ˜MšŸœŸœ˜M˜—šŸœ Ÿ˜Mšœ˜Mšœ˜Mšœ#˜#Mšœ!˜!Mšœ˜Mšœ!˜!Mšœ˜MšœŸœ˜MšŸœŸœ˜—MšœŸœ˜0MšœU˜UMšœK˜KMšœ*˜*MšŸœ˜—Mšœ™š ‘œŸ œŸœ ŸœŸœŸœ˜ZM™ŽMšœŸœ˜&MšœŸœ˜šŸœ(Ÿœ˜0MšœŸœŸœ˜GMšœŸœ˜'MšœŸœ˜'M˜,MšœŸœŸœŸœ˜fMšœ˜—šŸœ˜M˜MšœŸœ(Ÿœ ˜^MšœŸ˜—šŸœŸœ˜Mšœ ˜ Mšœ!˜!Mšœ˜—M˜—šœŸœŸœŸœ˜?Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ+˜+Mšœ˜Mšœ˜—MšœŸœŸœ Ÿœ˜Gš‘œŸœŸœŸœ˜3MšœŸœŸœ˜5Mšœ$˜$—š ‘ œŸœŸœŸœŸœ˜5MšœŸœŸœ˜5Mšœ[™[MšŸœ Ÿœ ˜3—š ‘œŸœŸœŸœ Ÿœ˜@MšœŸœŸœ˜5MšŸœ ˜—š‘œŸœŸœ Ÿœ˜5MšœŸœŸœ˜5Mš ŸœŸœŸœŸœŸœ˜HM˜&—š‘œŸœŸœ ŸœŸœŸœ ŸœŸœŸœŸœŸœ˜€MšœŸœŸœ˜5Mšœ Ÿœ*˜8Mšœ ŸœŸœ˜)MšœŸœ˜"šœ)˜)Mšœ@˜@——š ‘œŸœŸœŸœŸœ˜7MšœŸœŸœ˜5Mšœ Ÿœ˜(MšŸœ!Ÿœ˜=MšŸœ˜ —š‘œŸœŸœŸœ ŸœŸœŸœ˜ZMšœ Ÿœ ˜/MšœŸœ˜MšœR˜Ršœ3˜3MšŸœŸœ ˜&——M™šœ;˜;Mšœ˜Mšœ ˜ —Mšœ<Ÿœ˜AMšœ$˜$Mšœ/˜/Mšœ,˜,MšŸœ˜——…—Hd@