DIRECTORY BasicTime USING [GMT, Now, Period, Update], Convert USING [Error, IntFromRope, RopeFromInt, RopeFromRope, TimeFromRope], FS USING [Copy, Delete, EnumerateForInfo, Error, InfoProc, Rename, StreamOpen], IO USING [BreakProc, Close, EndOfStream, GetBlock, GetIndex, GetLineRope, GetTokenRope, int, Put, PutBlock, PutChar, PutF, PutFR, PutRope, rope, RopeFromROS, ROS, SetIndex, SetLength, STREAM, time], RefText USING [ObtainScratch], Rope USING [Cat, Concat, Equal, Fetch, Find, Length, ROPE, Substr], SMTPControl USING [deadLetterSenderName, deadLetterPath, itemExpiryTimeout], SMTPDescr USING [Descr, Format, HostProc, InitialItemProc, PrintForm, RawRecipProc], SMTPSupport USING [CreateSubrangeStream, Log, LogPriority, Now, RFC822Date, RopeFromSubrange]; SMTPDescrImpl: CEDAR MONITOR IMPORTS BasicTime, Convert, FS, IO, RefText, Rope, SMTPControl, SMTPSupport EXPORTS SMTPDescr = BEGIN Descr: TYPE = REF DescrRep; DescrRep: PUBLIC TYPE = RECORD[ userHandle: INT, source: ROPE, format: SMTPDescr.Format, arpaReversePath: ROPE, gvSender: ROPE, recipients: RecipientList, createDate: BasicTime.GMT, expiryDate: BasicTime.GMT, precedeMsgText: ROPE, returnPathLine: ROPE, fileName: ROPE, infoStartIndex: INT]; RecipientList: TYPE = REF RecipientListRep; RecipientState: TYPE = {raw, processed}; RecipientListRep: TYPE = RECORD[ val: SELECT state: RecipientState FROM raw => [raw: LIST OF ROPE], processed => [processed: LIST OF HostAndUsers], ENDCASE]; HostAndUsers: TYPE = RECORD[host: ROPE, users: LIST OF ROPE]; valuesRecipientState: ARRAY RecipientState OF ROPE = [raw: "raw", processed: "processed"]; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; twoDays: INT = INT[2]*24*60*60; Create: PUBLIC PROC [ arpaReversePath: ROPE _ NIL, gvSender: ROPE _ NIL, rawRecipients: LIST OF ROPE, source: ROPE _ NIL, format: SMTPDescr.Format, msgStream: STREAM, precedeMsgText: ROPE _ NIL, returnPathLine: ROPE _ NIL] RETURNS [descr: Descr] = TRUSTED { expiryDate: BasicTime.GMT; expiryDate _ BasicTime.Update[BasicTime.Now[], SMTPControl.itemExpiryTimeout]; descr _ NEW[DescrRep _ [ userHandle: UniqueUserHandle[], arpaReversePath: arpaReversePath, gvSender: gvSender, recipients: NEW[raw RecipientListRep _[raw[rawRecipients]]], createDate: BasicTime.Now[], expiryDate: expiryDate, source: source, format: format, precedeMsgText: precedeMsgText, returnPathLine: returnPathLine, fileName: UniqueFileName[], infoStartIndex: -1 --undefined--]]; StoreEntireItem[descr, msgStream -- includes sucking message from msgStream ! BadItemFile => ERROR CreateFailed]; }; -- end Create CopyForReturn: PUBLIC PROC [old: Descr, reason: ROPE] RETURNS [new: Descr] = { newFileName: ROPE = UniqueFileName[]; expiryDate: BasicTime.GMT; sender: ROPE; date: ROPE _ Rope.Cat["Date: ", SMTPSupport.RFC822Date[BasicTime.Now[]], "\n"]; from: ROPE; to: ROPE; subject: ROPE _ "Subject: Undeliverable mail\n\n"; header: ROPE; arpaReversePath: ROPE; [] _ FS.Copy[old.fileName, newFileName ! FS.Error => { SMTPSupport.Log[ important, "Failed to copy file ", old.fileName, " to ", newFileName, "\nwhen trying to create new descriptor to return item to sender.\n", "FS said ", error.explanation, "."]; ERROR CreateFailed}]; SELECT old.format FROM gv => { sender _ old.gvSender; from _ SMTPControl.deadLetterSenderName; -- Mailer.PA IF Rope.Find[sender, "@"] # -1 THEN from _ SMTPControl.deadLetterPath; -- Mailer.PA@Xerox.ARPA arpaReversePath _ SMTPControl.deadLetterPath; }; arpa => { sender _ old.arpaReversePath; from _ SMTPControl.deadLetterPath; arpaReversePath _ SMTPControl.deadLetterSenderName; }; -- SMTPSendImpl will add @Xerox ENDCASE => ERROR; IF sender.Fetch[0] # '@ THEN to _ Rope.Cat["To: ", sender, "\n"] ELSE to _ Rope.Cat["To: RFC822.Requires.Something.Here <", sender, ">\n"]; from _ Rope.Cat["From: ", from, "\n"]; header _ Rope.Cat[date, from, to, subject, reason]; IF old.returnPathLine # NIL THEN header _ Rope.Cat[header, old.returnPathLine, "\n"]; expiryDate _ BasicTime.Update[BasicTime.Now[], SMTPControl.itemExpiryTimeout]; new _ NEW[DescrRep _ [ userHandle: UniqueUserHandle[], arpaReversePath: arpaReversePath, gvSender: SMTPControl.deadLetterSenderName, recipients: NEW[raw RecipientListRep _ [raw[LIST[sender]]]], createDate: BasicTime.Now[], expiryDate: expiryDate, source: NIL, format: old.format, precedeMsgText: Rope.Cat[header, old.precedeMsgText], returnPathLine: NIL, fileName: newFileName, infoStartIndex: old.infoStartIndex]]; StoreItemInfo[new]; }; -- end CopyForReturn GetUserHandle: PUBLIC PROC [self: Descr] RETURNS [INT] = { RETURN[self.userHandle] }; GetFileName: PUBLIC PROC [self: Descr] RETURNS [ROPE] = { RETURN[self.fileName]; }; GetExpiryDate: PUBLIC PROC [self: Descr] RETURNS [BasicTime.GMT] = { RETURN[self.expiryDate] }; GetArpaReversePath: PUBLIC PROC [self: Descr] RETURNS [path: ROPE] = { RETURN[self.arpaReversePath]; }; SetArpaReversePath: PUBLIC PROC [self: Descr, path: ROPE] = { self.arpaReversePath _ path; }; GetGvSender: PUBLIC PROC [self: Descr] RETURNS [rName: ROPE] = { RETURN[self.gvSender]; }; SetGvSender: PUBLIC PROC [self: Descr, rName: ROPE] = { self.gvSender _ rName; }; EnumerateRawRecipients: PUBLIC PROC [self: Descr, proc: SMTPDescr.RawRecipProc, procData: REF ANY _ NIL] = { recipients: LIST OF ROPE; IF NOT ISTYPE[self.recipients, REF raw RecipientListRep] THEN ERROR WrongState; recipients _ NARROW[self.recipients, REF raw RecipientListRep].raw; FOR restRecips: LIST OF ROPE _ recipients, restRecips.rest UNTIL restRecips = NIL DO IF NOT proc[restRecips.first, procData] THEN RETURN; ENDLOOP; }; RemoveRawRecipients: PUBLIC PROC [self: Descr] = { IF NOT ISTYPE[self.recipients, REF raw RecipientListRep] THEN ERROR WrongState; NARROW[self.recipients, REF raw RecipientListRep].raw _ NIL; -- test this StoreItemInfo[self]; }; AddProcessedRecipient: PUBLIC PROC [self: Descr, hostName: ROPE, userName: ROPE, checkDuplicate: BOOL] = { WITH self.recipients SELECT FROM raw: REF raw RecipientListRep => self.recipients _ NEW[processed RecipientListRep _ [processed[LIST[HostAndUsers[hostName, LIST[userName]]]]]]; processed: REF processed RecipientListRep => { hostList: LIST OF HostAndUsers _ processed.processed; FOR restHosts: LIST OF HostAndUsers _ hostList, restHosts.rest UNTIL restHosts = NIL DO hostAndUsers: HostAndUsers = restHosts.first; IF Rope.Equal[hostAndUsers.host, hostName, FALSE] THEN { userNames: LIST OF ROPE _ hostAndUsers.users; IF checkDuplicate THEN FOR userList: LIST OF ROPE _ userNames, userList.rest UNTIL userList = NIL DO IF Rope.Equal[userList.first, userName] THEN EXIT; REPEAT FINISHED => userNames _ CONS[userName, userNames]; ENDLOOP ELSE -- just add this user name at end userNames _ CONS[userName, userNames]; restHosts.first.users _ userNames; EXIT; }; REPEAT FINISHED => -- not yet a HostAndUsers element for this host; create one processed.processed _ CONS[HostAndUsers[hostName, LIST[userName]], hostList]; ENDLOOP; }; ENDCASE; }; -- end AddProcessedRecipient RemoveRecipientHost: PUBLIC PROC [self: Descr, hostName: ROPE] = { recipients: REF processed RecipientListRep; hostList: LIST OF HostAndUsers; IF NOT ISTYPE[self.recipients, REF processed RecipientListRep] THEN ERROR WrongState; recipients _ NARROW[self.recipients, REF processed RecipientListRep]; hostList _ recipients.processed; IF Rope.Equal[hostList.first.host, hostName, FALSE] THEN hostList _ hostList.rest ELSE { lastList: LIST OF HostAndUsers _ hostList; FOR list: LIST OF HostAndUsers _ hostList.rest, list.rest UNTIL list = NIL DO IF Rope.Equal[list.first.host, hostName, FALSE] THEN {lastList.rest _ list.rest; EXIT}; lastList _ list; ENDLOOP; }; recipients.processed _ hostList; self.recipients _ recipients; StoreItemInfo[self]; }; EnumerateRecipientHosts: PUBLIC PROC [self: Descr, proc: SMTPDescr.HostProc, procData: REF ANY _ NIL] = { recipients: LIST OF HostAndUsers; IF NOT ISTYPE[self.recipients, REF processed RecipientListRep] THEN ERROR WrongState; recipients _ NARROW[self.recipients, REF processed RecipientListRep].processed; FOR restHostRecipients: LIST OF HostAndUsers _ recipients, restHostRecipients.rest UNTIL restHostRecipients = NIL DO IF NOT proc[restHostRecipients.first.host, restHostRecipients.first.users, procData] THEN RETURN; ENDLOOP; }; MoreRecipients: PUBLIC PROC [self: Descr] RETURNS [BOOL] = { WITH self.recipients SELECT FROM raw: REF raw RecipientListRep => RETURN[raw.raw # NIL]; processed: REF processed RecipientListRep => RETURN[processed.processed # NIL]; ENDCASE => ERROR; }; GetFormat: PUBLIC PROC [self: Descr] RETURNS [SMTPDescr.Format] = { RETURN[self.format]; }; GetPrecedeMsgText: PUBLIC PROC [self: Descr] RETURNS [ROPE] = { RETURN[self.precedeMsgText]; }; SetPrecedeMsgText: PUBLIC PROC [self: Descr, new: ROPE] = { self.precedeMsgText _ new; }; GetReturnPathLine: PUBLIC PROC [self: Descr] RETURNS [ROPE] = { RETURN[self.returnPathLine]; }; GetSource: PUBLIC PROC [self: Descr] RETURNS [ROPE] = { IF self.source.Fetch[0] = '[ THEN RETURN[Rope.Cat[self.source, ".ARPA"]]; RETURN[self.source]; }; RetrieveMsgStream: PUBLIC PROC [self: Descr] RETURNS [STREAM] = { RETURN[OpenMsgFileStream[self.fileName, self.infoStartIndex ! BadItemFile => ERROR PutOnBadQueue]]; }; UniqueID: PUBLIC PROC [self: Descr] RETURNS [rope: ROPE] = { bang: INT; rope _ self.fileName; rope _ Rope.Substr[rope, itemFileNamePrefixLength]; -- Strip off leading "[]<>MG>Q>Item-" bang _ Rope.Find[rope, "!"]; IF bang = -1 THEN RETURN; rope _ Rope.Substr[rope, 0, bang-1]; }; Destroy: PUBLIC PROC[self: Descr] = { FS.Delete[self.fileName]; }; Print: PUBLIC PROC [descr: Descr, out: STREAM, form: SMTPDescr.PrintForm _ long] = { PutLine: PROC [r1, r2, r3, r4: ROPE _ NIL] = { out.PutRope[r1]; out.PutRope[r2]; out.PutRope[r3]; out.PutRope[r4]; out.PutChar['\n]; }; out.PutF["#%g (%g)\n", IO.int[descr.userHandle], IO.rope[descr.fileName]]; WITH descr.recipients SELECT FROM raw: REF raw RecipientListRep => { out.PutRope["Recipients (raw): "]; FOR names: LIST OF ROPE _ raw.raw, names.rest UNTIL names = NIL DO out.PutRope[names.first]; IF names.rest # NIL THEN out.PutRope[", "]; ENDLOOP; out.PutChar['\n]; }; processed: REF processed RecipientListRep => { out.PutRope["Recipients (processed): "]; FOR hosts: LIST OF HostAndUsers _ processed.processed, hosts.rest UNTIL hosts = NIL DO hostAndUsers: HostAndUsers = hosts.first; out.PutRope[hostAndUsers.host]; out.PutRope[": "]; FOR users: LIST OF ROPE _ hostAndUsers.users, users.rest UNTIL users = NIL DO out.PutRope[users.first]; IF users.rest # NIL THEN out.PutRope[", "]; ENDLOOP; IF hosts.rest # NIL THEN out.PutRope["; "]; ENDLOOP; out.PutChar['\n]; }; ENDCASE; PutLine["Arpa Reverse Path: ", descr.arpaReversePath]; PutLine["GV Sender: ", descr.gvSender]; out.PutF["Create Date: %g\n", IO.time[descr.createDate]]; out.PutF["Expiry Date: %g\n", IO.time[descr.expiryDate]]; PutLine["Item Format: ", valuesFormat[descr.format]]; PutLine["Precede Message Text: ", Convert.RopeFromRope[descr.precedeMsgText, TRUE]]; PutLine["Return Path Line: ", Convert.RopeFromRope[descr.returnPathLine, TRUE]]; }; -- end Print Unparse: PUBLIC PROC [descr: Descr, form: SMTPDescr.PrintForm _ short] RETURNS [ROPE] = { IF form = short THEN RETURN[IO.PutFR["Msg #%g (%g)", IO.int[descr.userHandle], IO.rope[descr.fileName]]] ELSE { printStream: STREAM = IO.ROS[]; Print[descr, printStream, long]; RETURN[IO.RopeFromROS[printStream]]; }; }; CreateFailed: PUBLIC ERROR = CODE; WrongState: PUBLIC ERROR = CODE; PutOnBadQueue: PUBLIC ERROR = CODE; itemFileNamePrefix: ROPE = "[]<>MG>Q>Item-"; itemFileNamePrefixLength: INT = Rope.Length[itemFileNamePrefix]; EnumerateInitialItems: PUBLIC PROC [proc: SMTPDescr.InitialItemProc] = { ForEachItemFileDo: FS.InfoProc = { fileOK: BOOL _ TRUE; simpleStart: INT; fileName: ROPE; descr: Descr; continue _ TRUE; IF BasicTime.Period[from: enumerationStarted, to: created] >= 0 THEN RETURN; IF Rope.Find[fullFName, "$"] > 0 THEN { -- Crash during reception FS.Delete[fullFName]; RETURN; }; simpleStart _ Rope.Find[fullFName, itemFileNamePrefix, 0, FALSE]; IF simpleStart < 0 THEN RETURN; -- shouldn't occur fileName _ Rope.Substr[fullFName, simpleStart]; descr _ RetrieveItemInfo[fileName ! BadItemFile => {fileOK _ FALSE; CONTINUE}]; IF fileOK THEN proc[descr]; RETURN; }; enumerationStarted: BasicTime.GMT = BasicTime.Now[]; FS.EnumerateForInfo[Rope.Concat[itemFileNamePrefix, "*!H"], ForEachItemFileDo]; }; ReadItemFile: PUBLIC PROC [fileName: ROPE] RETURNS [Descr] = { RETURN[RetrieveItemInfo[fileName ! BadItemFile => ERROR Failed[Rope.Cat["Unable to read ", fileName, " as a valid item file. (See log for more details.)"]]]]; }; Failed: PUBLIC ERROR [reason: ROPE] = CODE; titleInfoStartIndex: ROPE = "-------- Item Info starts at Position --------"; titleMsgBody: ROPE = "-------- Message Body --------"; titleRecipients: ROPE = "-------- Recipients --------"; titleInfoState: ROPE = "State:"; titleRecipientNames: ROPE = "Names:"; titleRecipientHost: ROPE = "Host:"; titleRecipientUsers: ROPE = "Users:"; titleReturnInfo: ROPE = "-------- Return Info --------"; titleReversePathVal: ROPE = "Path:"; titleDates: ROPE = "-------- Dates --------"; titleFormat: ROPE = "-------- Source and Format --------"; titlePrecedeMsgText: ROPE = "-------- Precede Message Text --------"; titleReturnPathLine: ROPE = "-------- Return Path Line --------"; valueStateRaw: ROPE = "raw"; valueStateProcessed: ROPE = "processed"; numPlaceHolder: ROPE = " "; -- Big enough for length of longest message placeHolderFormat: ROPE = IO.PutFR["%%%dd", IO.int[Rope.Length[numPlaceHolder]]]; valuesFormat: ARRAY SMTPDescr.Format OF ROPE _ [gv: "GV", arpa: "ARPA"]; StoreEntireItem: PROC [descr: Descr, msgStream: STREAM] = { tempName: ROPE = Rope.Cat[descr.fileName, "$"]; itemFile: STREAM _ NIL; BEGIN ENABLE { FS.Error => { SMTPSupport.Log[important, "Unable to open stream/store entire item to \"", descr.fileName, "\".\nFS reported \"", error.explanation, "\".\nI will reject the incoming mail item."]; ERROR BadItemFile; }; UNWIND => IF itemFile # NIL THEN {itemFile.Close[]; FS.Delete[tempName]}; }; PutLine: PROC [line: ROPE] = INLINE {itemFile.PutRope[line]; itemFile.PutChar['\n]}; indexIndex, nBytesRead: INT; buffer: REF TEXT; itemFile _ FS.StreamOpen[fileName: tempName, accessOptions: create]; PutLine[titleInfoStartIndex]; indexIndex _ itemFile.GetIndex[]; PutLine[numPlaceHolder]; PutLine[titleMsgBody]; buffer _ RefText.ObtainScratch[512]; DO nBytesRead _ msgStream.GetBlock[buffer]; -- catch stream failure signal (globally?) itemFile.PutBlock[block: buffer, count: nBytesRead]; IF nBytesRead < buffer.maxLength THEN EXIT; ENDLOOP; descr.infoStartIndex _ itemFile.GetIndex[]; itemFile.SetIndex[indexIndex]; itemFile.PutF[placeHolderFormat, IO.int[descr.infoStartIndex]]; itemFile.SetIndex[descr.infoStartIndex]; StoreItemInfoOnFileStream[descr, itemFile]; itemFile.Close[]; END; FS.Rename[from: tempName, to: descr.fileName]; }; -- end StoreEntireItem LookAtThis: SIGNAL = CODE; StoreItemInfo: PUBLIC PROC [descr: Descr] = { itemFile: STREAM _ NIL; BEGIN ENABLE { FS.Error => { IF error.group = lock THEN { SMTPSupport.Log[ important, "Unable to update item file (lock conflict): ", descr.fileName, ".\nFS reported: ", error.explanation, "This happens when we send a msg to GV and ARPA at the same time. Plunging on."]; CONTINUE; }; SIGNAL LookAtThis; SMTPSupport.Log[ ATTENTION, "Unable to open/update item file: ", descr.fileName, ".\nFS reported: ", error.explanation, "\nContinuing without saving the change (message may be multiply sent)."]; IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; CONTINUE; }; UNWIND => IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; }; itemFile _ FS.StreamOpen[fileName: descr.fileName, accessOptions: write]; StoreItemInfoOnFileStream[descr, itemFile]; END; }; StoreItemInfoOnFileStream: PROC [descr: Descr, itemFile: STREAM] = { Newline: PROC = INLINE {itemFile.PutChar['\n]}; PutLine: PROC [line: ROPE] = INLINE {itemFile.PutRope[line]; Newline[]}; endIndex, textEndIndexIndex: INT; itemFile.SetIndex[descr.infoStartIndex]; PutLine[NIL]; -- separate from msg w/newline PutLine[titleRecipients]; PutLine[titleInfoState]; WITH descr.recipients SELECT FROM raw: REF raw RecipientListRep => { numNames: INT _ 0; numNamesIndex: INT; PutLine[valuesRecipientState[raw]]; PutLine[titleRecipientNames]; numNamesIndex _ itemFile.GetIndex[]; PutLine[numPlaceHolder]; FOR names: LIST OF ROPE _ raw.raw, names.rest UNTIL names = NIL DO PutLine[names.first]; numNames _ numNames + 1; ENDLOOP; endIndex _ itemFile.GetIndex[]; itemFile.SetIndex[numNamesIndex]; itemFile.PutF[placeHolderFormat, IO.int[numNames]]; itemFile.SetIndex[endIndex]; }; processed: REF processed RecipientListRep => { numHosts: INT _ 0; numHostsIndex: INT; PutLine[valuesRecipientState[processed]]; numHostsIndex _ itemFile.GetIndex[]; PutLine[numPlaceHolder]; FOR hosts: LIST OF HostAndUsers _ processed.processed, hosts.rest UNTIL hosts = NIL DO numUsers: INT _ 0; numUsersIndex: INT; hostAndUsers: HostAndUsers = hosts.first; PutLine[titleRecipientHost]; PutLine[hostAndUsers.host]; PutLine[titleRecipientUsers]; numUsersIndex _ itemFile.GetIndex[]; PutLine[numPlaceHolder]; FOR users: LIST OF ROPE _ hostAndUsers.users, users.rest UNTIL users = NIL DO PutLine[users.first]; numUsers _ numUsers + 1; ENDLOOP; endIndex _ itemFile.GetIndex[]; itemFile.SetIndex[numUsersIndex]; itemFile.PutF[placeHolderFormat, IO.int[numUsers]]; itemFile.SetIndex[endIndex]; numHosts _ numHosts + 1; ENDLOOP; endIndex _ itemFile.GetIndex[]; itemFile.SetIndex[numHostsIndex]; itemFile.PutF[placeHolderFormat, IO.int[numHosts]]; itemFile.SetIndex[endIndex]; }; ENDCASE; PutLine[titleReturnInfo]; PutLine[descr.arpaReversePath]; PutLine[descr.gvSender]; PutLine[titleDates]; itemFile.Put[IO.time[descr.createDate]]; Newline[]; itemFile.Put[IO.time[descr.expiryDate]]; Newline[]; PutLine[titleFormat]; PutLine[descr.source]; PutLine[valuesFormat[descr.format]]; PutLine[titlePrecedeMsgText]; textEndIndexIndex _ itemFile.GetIndex[]; PutLine[numPlaceHolder]; itemFile.PutRope[descr.precedeMsgText]; -- may contain newlines endIndex _ itemFile.GetIndex[]; itemFile.SetIndex[textEndIndexIndex]; itemFile.PutF[placeHolderFormat, IO.int[endIndex]]; itemFile.SetIndex[endIndex]; PutLine[NIL]; -- ensure separated from next field by newline PutLine[titleReturnPathLine]; itemFile.PutRope[descr.returnPathLine]; -- may contain newlines itemFile.SetLength[itemFile.GetIndex[]]; itemFile.Close[]; }; -- end StoreItemInfo OpenMsgFileStream: PROC [fileName: ROPE, maxMsgIndex: INT] RETURNS [STREAM] = { itemFile: STREAM _ NIL; BEGIN ENABLE { FS.Error => { SMTPSupport.Log[ATTENTION, "Unable to open stream/read message body from \"", fileName, "\".\nFS reported \"", error.explanation, "\".\nI will place descriptor on BadQueue. ", "Edit file and Requeue descriptor."]; IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; ERROR BadItemFile; }; BadItemFile => { SMTPSupport.Log[ATTENTION, "Corrupt item file \"", fileName, "\", at Position ", Convert.RopeFromInt[position], ".\nExpected \"", expected, "\", but found \"", found, "\".\nI will place descriptor on BadQueue. Edit file and Requeue descriptor."]; IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; ERROR BadItemFile; }; UNWIND => IF itemFile # NIL THEN itemFile.Close[]; }; GetLine: PROC [expected: ROPE] RETURNS [ROPE] = { RETURN[itemFile.GetLineRope[! IO.EndOfStream => ERROR BadItemFile[expected, "EndOfFile", itemFile.GetIndex[]]]] }; -- catches all EOFs since all reading done here CheckLine: PROC [expected: ROPE] = { found: ROPE = GetLine[expected]; IF NOT Rope.Equal[expected, found] THEN ERROR BadItemFile[expected, found, itemFile.GetIndex[]-(found.Length[]+1)]; }; itemFile: IO.STREAM _ FS.StreamOpen[fileName]; CheckLine[titleInfoStartIndex]; [] _ GetLine["Info Start Index"]; CheckLine[titleMsgBody]; RETURN[SMTPSupport.CreateSubrangeStream[ origStream: itemFile, min: itemFile.GetIndex[], max: maxMsgIndex]]; END; }; -- end OpenMsgFileStream RetrieveItemInfo: PUBLIC PROC [fileName: ROPE] RETURNS [descr: Descr] = { itemFile: STREAM _ NIL; BEGIN ENABLE { FS.Error => { SMTPSupport.Log[ATTENTION, "Unable to open/read item file \"", fileName, "\".\nFS reported \"", error.explanation, "\".\nEdit file and InitRead descriptor."]; IF itemFile # NIL THEN itemFile.Close[]; ERROR BadItemFile; -- caught by EnumerateInitialItems and ReadItemFile }; BadItemFile => { SMTPSupport.Log[ATTENTION, "Corrupt item file \"", fileName, "\", at Position ", Convert.RopeFromInt[position], ".\nExpected \"", expected, "\", but found \"", found, "\".\nIgnoring it; edit it and reread."]; IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; ERROR BadItemFile; -- caught by EnumerateInitialItems and ReadItemFile }; UNWIND => IF itemFile # NIL THEN {itemFile.Close[]; itemFile _ NIL}; }; GetLine: PROC [expected: ROPE] RETURNS [ROPE] = { RETURN[itemFile.GetLineRope[! IO.EndOfStream => ERROR BadItemFile[expected, "EndOfFile", itemFile.GetIndex[]]]] }; -- catches all EOFs since all reading done here CheckLine: PROC [expected: ROPE] = { found: ROPE = GetLine[expected]; IF NOT Rope.Equal[expected, found] THEN ERROR BadItemFile[expected, found, itemFile.GetIndex[]-(found.Length[]+1)]; }; GetInt: PROC [intName: ROPE] RETURNS [INT] = { found: ROPE = GetLine[intName]; RETURN[Convert.IntFromRope[found ! Convert.Error => ERROR BadItemFile[intName, found, itemFile.GetIndex[]-(found.Length[]+1)]]]; }; GetRecipientState: PROC RETURNS [RecipientState] = { expected: ROPE = "info state value (\"raw\" or (\"processed\")"; state: ROPE = GetLine[expected]; IF Rope.Equal[state, valuesRecipientState[raw]] THEN RETURN[raw]; IF Rope.Equal[state, valuesRecipientState[processed]] THEN RETURN[processed]; ERROR BadItemFile[expected, state, itemFile.GetIndex[]-(found.Length[]+1)]; }; EverythingProc: IO.BreakProc = {RETURN[other]}; recipients: RecipientList; arpaReversePath, gvSender: ROPE; createDate, expiryDate: BasicTime.GMT; source: ROPE; format: SMTPDescr.Format; returnPathLine: ROPE; precedeMsgText: ROPE; infoStartIndex: INT; textEndIndex: INT; expected, found: ROPE; itemFile _ FS.StreamOpen[fileName: fileName]; itemFile.SetIndex[0]; CheckLine[titleInfoStartIndex]; infoStartIndex _ GetInt["Info Start Index"]; itemFile.SetIndex[infoStartIndex]; CheckLine[NIL]; -- separated from msg w/newline CheckLine[titleRecipients]; CheckLine[titleInfoState]; SELECT GetRecipientState[] FROM raw => { names: LIST OF ROPE _ NIL; CheckLine[titleRecipientNames]; THROUGH [0..GetInt["number of recipient names (INT)"]) DO names _ CONS[GetLine["recipient name"], names]; ENDLOOP; recipients _ NEW[raw RecipientListRep _ [raw[names]]]; }; processed => { hosts: LIST OF HostAndUsers _ NIL; THROUGH [0..GetInt["number of recipient hosts (INT)"]) DO host: ROPE; users: LIST OF ROPE _ NIL; CheckLine[titleRecipientHost]; host _ GetLine["recipient host name"]; CheckLine[titleRecipientUsers]; THROUGH [0..GetInt["number of users for given host (INT)"]) DO users _ CONS[GetLine["recipient user name"], users]; ENDLOOP; hosts _ CONS[HostAndUsers[host, users], hosts]; ENDLOOP; recipients _ NEW[processed RecipientListRep _ [processed[hosts]]]; }; ENDCASE; CheckLine[titleReturnInfo]; expected _ "ARPA Return Path"; arpaReversePath _ GetLine[expected]; IF arpaReversePath.Length[] = 0 THEN arpaReversePath _ NIL; expected _ "GV Sender"; gvSender _ GetLine[expected]; IF gvSender.Length[] = 0 THEN gvSender _ NIL; CheckLine[titleDates]; expected _ "Create date"; found _ GetLine[expected]; createDate _ Convert.TimeFromRope[found ! Convert.Error => ERROR BadItemFile[expected, found, itemFile.GetIndex[]-(found.Length[]+1)]]; expected _ "Expiry date"; found _ GetLine[expected]; expiryDate _ Convert.TimeFromRope[found ! Convert.Error => ERROR BadItemFile[expected, found, itemFile.GetIndex[]-(found.Length[]+1)]]; CheckLine[titleFormat]; expected _ "Source IP Address ([nnn.nnn.nnn.nnn])"; source _ GetLine[expected]; expected _ "item format (\"ARPA\" or \"GV\")"; found _ GetLine[expected]; SELECT TRUE FROM Rope.Equal[found, valuesFormat[arpa]] => format _ arpa; Rope.Equal[found, valuesFormat[gv]] => format _ gv; ENDCASE => ERROR BadItemFile[expected, found, itemFile.GetIndex[]-found.Length[]-1]; CheckLine[titlePrecedeMsgText]; textEndIndex _ GetInt["Precede Msg Text end index"]; precedeMsgText _ SMTPSupport.RopeFromSubrange[ -- Empty => NIL origStream: itemFile, min: itemFile.GetIndex[], max: textEndIndex]; CheckLine[NIL]; CheckLine[titleReturnPathLine]; returnPathLine _ NIL; -- In case of empty returnPathLine _ itemFile.GetTokenRope[EverythingProc ! IO.EndOfStream => CONTINUE].token; itemFile.Close[]; descr _ NEW[DescrRep _ [ userHandle: UniqueUserHandle[], arpaReversePath: arpaReversePath, gvSender: gvSender, recipients: recipients, createDate: createDate, expiryDate: expiryDate, source: source, format: format, precedeMsgText: precedeMsgText, returnPathLine: returnPathLine, fileName: fileName, infoStartIndex: infoStartIndex]]; RETURN[descr]; END; }; BadItemFile: ERROR [expected, found: ROPE _ NIL, position: INT _ 0] = CODE; UniqueUserHandle: ENTRY PROC RETURNS [INT] = { nextUserHandle _ nextUserHandle + 1; RETURN[nextUserHandle]; }; UniqueFileName: ENTRY PROC RETURNS [ROPE] = { timeLiteral: ROPE; [timeLiteral, ] _ SMTPSupport.Now[compressed: TRUE]; timeStampPostfix _ timeStampPostfix + 1; RETURN[Rope.Cat[ itemFileNamePrefix, timeLiteral, "-", Convert.RopeFromInt[timeStampPostfix]]]; }; timeStampPostfix: INT _ 1000; nextUserHandle: INT _ 1000; END. SMTPDescrImpl.mesa Hal Murray May 28, 1985 4:52:46 pm PDT Last Edited by: HGM, October 2, 1984 3:08:59 am PDT Last Edited by: DCraft, December 21, 1983 3:38 pm Last Edited by: Taft, January 23, 1984 1:35:48 pm PST John Larson, March 9, 1987 12:24:13 pm PST ---- Descriptors ----- Type Definitions and Instance Creation Copy the old file to the new one. Create a new descriptor and write out the (changed) item info. Rejecting ARPA recipient in GV DL, but sender came in from ARPA Field Selection and Setting start a new processed recipient list (EnumerateRawRecipients will already have a handle on the raw one) add to the existing one Correct host list found; add this user as appropriate. Check each user name, adding this at end if not found. StoreItemInfo: PUBLIC PROC [self: Descr] = see below ===== Recipients ===== ===== Return Info ===== ===== Dates ===== ===== Item Format ===== ===== Precede Message Text ===== ===== Return Path Line ===== ----- Filing System ----- Initialization PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOLEAN]; Don't enumerate files created after the enumeration began (e.g. error report items to mgrs). Remove the path prefix from filename so it will Rope.Equal what user types to ReadItemFile. Item Storage ===== Info Start Index ===== ===== Message Body ===== ===== Info Start Index (continued) ===== ===== Recipients ===== ===== Return Info ===== ===== Dates ===== ===== Source and Format ===== ===== Precede Message Text ===== ===== Return Path Line ===== ItemRetrieval ===== Info Start Index ===== ===== Message Body ===== ===== Info Start Index ===== ===== Recipients ===== ===== Return Info ===== ===== Dates ===== ===== Item Format ===== ===== Precede Message Text ===== ===== Return Path Line ===== Misc Κ‹– "cedar" style˜head™code™&J™3J™1J™5—L™*code2šΟk ˜ Mšœ œœ˜+Mšœœ?˜LMšœœG˜OMšœœ–œœ˜ΖMšœœ˜Mšœœ+œ ˜DMšœ œ;˜LMšœ œE˜TMšœ œM˜^——šΟl œœ˜š˜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š œœœœ œœœ˜=Mšœœœœ(˜ZMšœœœ˜Mšœœœœ˜Mšœ œœ ˜šΟnœœœ˜Mšœœœ˜Mšœ œœ˜Mšœœœœ˜Mšœœœ˜Mšœ˜Mšœ œ˜Mšœœœ˜Mšœœœ˜Mšœœ˜"Mšœœ˜MšœN˜Nšœœ ˜Mšœ˜Mšœ!˜!Mšœ˜Mšœ œ-˜šœ ˜šœ˜Mšœ˜Mšœ5˜5šœ˜#Mš ?™?Mšœ:˜:—Mšœ0˜0—šœ ˜ Mšœ˜Mšœ"˜"MšœV˜V—Mšœœ˜—Mšœœ#˜@MšœF˜JMšœ&˜&Mšœ3˜3Mšœœœ5˜UMšœN˜Nšœœ ˜Mšœ˜Mšœ!˜!Mšœ+˜+Mšœ œœ ˜œœ˜Lšœœ ˜AMšœ˜Mšœ˜ —Mšœ[™[Mšœ:œ˜AMšœœœ ˜2Mšœ/˜/Mšœ=œœ˜OMšœœ ˜Mšœ˜M˜—Mšœœ˜4MšœM˜OM˜—š Ÿ œœœ œœ ˜>Mšœ-œh˜ M˜—Mš œœœ œœ˜+M™ Mšœœ4˜MMšœœ$˜6Mšœœ"˜7Mšœœ ˜ Mšœœ ˜%Mšœœ ˜#Mšœœ ˜%Mšœœ#˜8Mšœœ ˜$Mšœ œ˜-Mšœ œ)˜:Mšœœ,˜EMšœœ(˜AMšœœ ˜Mšœœ˜(MšœœΟiΠci+˜OMšœœœœ#˜QMšœœœœ˜IšŸœœœ˜;Mšœ œ!˜/Mšœ œœ˜Mš˜šœ˜šœ ˜ MšœΈ˜ΈMšœ˜—Mš œœ œœœ˜IM˜—M˜MšŸœœœœ1˜TMšœœ˜Mšœœœ˜Mšœ œ7˜DM˜Mšœ™M˜M˜!M˜M˜M™Mšœ˜M˜$š˜Mšœ) *˜SMšœ4˜4Mšœœœ˜+Mšœ˜—M˜M˜Mšœ(™(M˜+M˜Mšœ!œ˜?M˜(M˜Mšœ,˜,Mšœ˜Mšœ˜Mšœ,˜.Mšœ ˜—Mšœ œœ˜šŸ œœœ˜-Mšœ œœ˜Mš˜šœ˜šœ ˜ šœ˜šœ˜Mšœ ˜ Mšœ@˜@Mšœ&˜&MšœQ˜Q—Mšœ˜ —Mšœ ˜šœ˜Mš œ˜ Mšœ5˜5Mšœ&˜&MšœJ˜J—Mšœ œœœ˜:Mšœ˜ Mšœ˜—Mš œœ œœœ˜DM˜—M™Mšœ œ<˜IMšœ+˜+Mšœ˜M˜—šŸœœœ˜DMšŸœœœ˜/MšŸœœœœ%˜HMšœœ˜!M˜(M™M™Mšœœ ˜,M˜M˜šœœ˜!šœœ˜"Mšœ œ˜Mšœœ˜Mšœ#˜#Mšœ˜M˜$Mšœ˜š œœœœœ œ˜BM˜M˜Mšœ˜—M˜M˜!Mšœ!œ˜3M˜M˜—šœ œ ˜.Mšœ œ˜Mšœœ˜Mšœ)˜)M˜$Mšœ˜š œœœ0œ œ˜VMšœ œ˜Mšœœ˜Mšœ)˜)M˜Mšœ˜M˜M˜$Mšœ˜š œœœœ"œ œ˜MM˜M˜Mšœ˜—M˜M˜!Mšœ!œ˜3M˜M˜Mšœ˜—M˜M˜!Mšœ!œ˜3M˜M˜—Mšœ˜—M˜M™Mšœ˜Mšœ˜Mšœ˜M˜M™M˜Mšœ œ$˜3Mšœ œ$˜3M˜M™M˜Mšœ˜Mšœ$˜$M˜M™ M˜M˜(M˜Mšœ( ˜?M˜M˜%Mšœ!œ˜3M˜Mšœœ .˜Mšœœ(˜4Mšœ˜—Mšœœ#˜/Mšœ˜—Mšœ œ2˜BMšœ˜—Mšœ˜—M˜M™Mšœ˜Mšœ˜Mšœ$˜$Mšœœœ˜;Mšœ˜Mšœ˜Mšœœ œ˜-M˜M™M˜Mšœ˜Mšœ˜Mšœ<œG˜ˆMšœ˜Mšœ˜Mšœ<œG˜ˆM™M™M˜M˜3Mšœ˜Mšœ.˜.M˜šœœ˜Mšœ7˜7Mšœ3˜3MšœœD˜T—M˜M™ M˜M˜4šœ/ ˜>MšœC˜C—Mšœ œ˜M˜M™M˜Mšœœ ˜)Mšœ8œœ˜ZM˜M˜šœœ ˜Mšœ˜Mšœ!˜!Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ˜Mšœ ˜ Mšœ˜Mšœ˜Mšœ!˜!—Mšœ˜Mšœ˜—Mš œ œœœ œœ˜KM™š Ÿœœœœœ˜.Mšœ$˜$Mšœ˜M˜—š Ÿœœœœœ˜-Mšœ œ˜Mšœ.œ˜4Mšœ(˜(šœ ˜MšœN˜N—M˜—Mšœœ˜Mšœœ˜Mšœ˜——…—cȈq