DIRECTORY BasicTime USING [earliestGMT, GMT, Now, nullGMT], Char USING [Set], Convert USING [Error, RopeFromTimeRFC822, TimeFromRope], IO, MailBasics, MailBasicsItemTypes, NodeProps, Process USING [PauseMsec], Rope, MailUtils, MailUtilsBackdoor, TextEdit, TextNode, Tioga, TiogaIO, ViewerTools USING [TiogaContents, TiogaContentsRec]; MailUtilsImpl: CEDAR MONITOR IMPORTS BasicTime, Char, Convert, IO, Process, Rope, NodeProps, TextEdit, TextNode, TiogaIO EXPORTS MailUtils, MailUtilsBackdoor ~ BEGIN OPEN MailUtils; ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE = IO.STREAM; CredentialsProcs: TYPE = RECORD [ which: ATOM, proc: UserCredentialsProc, loggedIn: LoggedInUserProc, local: LocalNameProc, who: WhoIsLoggedInProc ]; GetUserCredentials: PUBLIC ENTRY PROC [which: ATOM] RETURNS [creds: LIST OF MailUtils.Credentials] ~ { ENABLE UNWIND => NULL; FOR ucL: LIST OF CredentialsProcs ¬ userCredentialsProcList, ucL.rest UNTIL ucL=NIL DO this: Credentials ¬ NIL; IF ( which = NIL ) OR ( which = ucL.first.which ) THEN this ¬ ucL.first.proc[]; IF this # NIL THEN creds ¬ CONS[this, creds]; ENDLOOP; }; userCredentialsProcList: LIST OF CredentialsProcs ¬ NIL; RegisterUserCredentialsProc: PUBLIC ENTRY PROC [which: ATOM, proc: UserCredentialsProc, loggedIn: LoggedInUserProc, local: LocalNameProc, who: WhoIsLoggedInProc] = { ENABLE UNWIND => NULL; userCredentialsProcList ¬ CONS[[which, proc, loggedIn, local, who], userCredentialsProcList]; }; IsThisTheLoggedInUser: PUBLIC ENTRY PROC [cdL: LIST OF Credentials] RETURNS [yes: BOOL] = { ENABLE UNWIND => NULL; atLeastOne: BOOL ¬ FALSE; IF userCredentialsProcList = NIL THEN RETURN[FALSE]; yes ¬ TRUE; FOR cL: LIST OF Credentials ¬ cdL, cL.rest UNTIL cL=NIL DO FOR ucL: LIST OF CredentialsProcs ¬ userCredentialsProcList, ucL.rest UNTIL ucL=NIL DO this: BOOL; IF ( cL.first.rName.ns # ucL.first.which ) THEN LOOP; this ¬ ucL.first.loggedIn[cL.first]; yes ¬ yes AND this; atLeastOne ¬ TRUE; ENDLOOP; ENDLOOP; RETURN[atLeastOne AND yes]; }; LocalNameFromRName: PUBLIC ENTRY LocalNameProc = { ENABLE UNWIND => NULL; FOR ucL: LIST OF CredentialsProcs ¬ userCredentialsProcList, ucL.rest UNTIL ucL = NIL DO IF ( rName.ns # ucL.first.which ) THEN LOOP; IF ( local ¬ ucL.first.local[rName] ) # NIL THEN RETURN; ENDLOOP; }; GetLoggedInUser: PUBLIC ENTRY PROC[which: ATOM] RETURNS[who: ROPE]= { ENABLE UNWIND => NULL; FOR ucL: LIST OF CredentialsProcs ¬ userCredentialsProcList, ucL.rest UNTIL ucL = NIL DO IF ( which # ucL.first.which ) THEN LOOP; IF ( who ¬ ucL.first.who[] ) # NIL THEN RETURN; ENDLOOP; }; GeneratePostmark: PUBLIC PROC [gmt: BasicTime.GMT ¬ BasicTime.nullGMT, machine: ROPE] RETURNS [ts: MailBasics.Postmark] ~ { tr: ROPE; msgNameFormat: ROPE = "$ %g@%g"; IF gmt=BasicTime.nullGMT THEN gmt ¬ BasicTime.Now[]; tr ¬ Convert.RopeFromTimeRFC822[gmt]; -- will cause problems soon [] ¬ Convert.TimeFromRope[tr ! Convert.Error => { tr ¬ Convert.RopeFromTimeRFC822[BasicTime.Now[]]; CONTINUE} ]; ts ¬ IO.PutFR[msgNameFormat, [rope[machine]], [rope[tr]]]; }; GetTimeFromPostmark: PUBLIC PROC [postmark: MailBasics.Postmark] RETURNS [time: BasicTime.GMT ¬ BasicTime.earliestGMT] ~ { tr: ROPE = Rope.Substr[base: postmark, start: Rope.Find[s1: postmark, s2: "@"]+1]; time ¬ Convert.TimeFromRope[tr ! Convert.Error => CONTINUE]; }; IsThisAPostmark: PUBLIC PROC[r: ROPE] RETURNS [yes: BOOLEAN] ~ { tr: ROPE; yes ¬ TRUE; IF Rope.Find[r, "$"] = -1 THEN RETURN [FALSE]; IF Rope.Find[r, "@"] = -1 THEN RETURN [FALSE]; tr ¬ Rope.Substr[base: r, start: Rope.Find[s1: r, s2: "@"]+1]; [] ¬ Convert.TimeFromRope[tr ! Convert.Error => {yes ¬ FALSE; CONTINUE}]; RETURN [yes]; }; RopeFromItemType: PUBLIC PROC [itemType: MailBasics.ItemType] RETURNS [rope: ROPE] ~ { OPEN MailBasicsItemTypes; rope ¬ SELECT itemType FROM envelope => "envelope", header => "header", vpFolder => "vpFolder", nsTextFile => "nsTextFile", vpDocument => "vpDocument", otherNSFile => "otherNSFile", multinationalNote => "multinationalNote", ia5Note => "ia5Note", pilotFile => "pilotFile", g3Fax => "g3Fax", teletex => "teletex", telex => "telex", iso6937Note => "iso6937Note", interpress => "interpress", plainTextForFormatting => "plainTextForFormatting", fullTextWithFormatting => "fullTextWithFormatting", vpFolderSerializedFile => "vpFolderSerializedFile", vpDocumentSerializedFile => "vpDocumentSerializedFile", nsTextFileSerializedFile => "nsTextFileSerializedFile", otherNSFileSerializedFile => "otherNSFileSerializedFile", interpressSerializedFile => "interpressSerializedFile", postscriptSerializedFile => "postscriptSerializedFile", postscript => "postscript", text => "text", tioga1 => "tioga1", tioga2 => "tioga2", walnutLog => "walnutLog", capability => "capability", audio => "audio", interscript => "interscript", messageComposer => "messageComposer", lastItem => "lastItem", ENDCASE => SELECT TRUE FROM itemType >= smalltalk AND itemType <= lastSmallTalk => IO.PutFR1["smalltalk(%g)", [cardinal[itemType]]], itemType >= interlisp AND itemType <= lastInterlisp => IO.PutFR1["interlisp(%g)", [cardinal[itemType]]], itemType >= gGW AND itemType <= lastGGW => IO.PutFR1["gGW(%g)", [cardinal[itemType]]], itemType >= cedar AND itemType <= lastCedar => IO.PutFR1["cedar(%g)", [cardinal[itemType]]], itemType >= voice AND itemType <= lastVoice => IO.PutFR1["voice(%g)", [cardinal[itemType]]], ENDCASE => IO.PutFR1["unknown(%g)", [cardinal[itemType]]]; }; RopeFromStream: PUBLIC PROC [s: STREAM, maxTextRun: INT ¬ 2048] RETURNS [contents: ROPE] ~ { text: Rope.Text ¬ Rope.NewText[maxTextRun]; pos: NAT ¬ 0; bytesRead: NAT ¬ 0; WHILE NOT IO.EndOf[s] DO TRUSTED {bytesRead ¬ IO.GetBlock[s, LOOPHOLE[text], pos, text.max]}; pos ¬ pos + bytesRead; IF pos >= text.max THEN { contents ¬ Rope.Concat[contents, text]; text ¬ Rope.NewText[maxTextRun]; pos ¬ 0; }; IF bytesRead = 0 AND NOT IO.EndOf[s] THEN Process.PauseMsec[200]; ENDLOOP; IF pos > 0 THEN contents ¬ Rope.Concat[contents, Rope.Substr[text, 0, pos]]; }; GetCRTiogaContents: PUBLIC PROC [root: TextNode.Ref] RETURNS [contents: ViewerTools.TiogaContents ¬ NIL] ~ { root.comment ¬ FALSE; NodeProps.PutProp[n: root, name: $NewlineDelimiter, value: "\r"]; FOR each: TextNode.Ref ¬ TextNode.StepForward[root], TextNode.StepForward[each] UNTIL each = NIL DO size: INT ~ Rope.Size[each.rope]; FOR index: INT ¬ Rope.SkipTo[s: each.rope, pos: 0, skip: "\l\r"], Rope.SkipTo[s: each.rope, pos: index+1, skip: "\l\r"] UNTIL index = size DO IF Char.Set[TextEdit.FetchChar[each, index]] = 0 THEN { [] ¬ TextEdit.ReplaceByRope[root: root, dest: each, start: index, len: 1, rope: "\r", looks: ALL[FALSE], charSet: 0]; } ELSE { [] ¬ TextEdit.ReplaceByRope[root: root, dest: each, rope: "X", start: index, len: 1, looks: ALL[FALSE], charSet: 0]; }; ENDLOOP; ENDLOOP; { pair: TiogaIO.Pair ¬ TiogaIO.ToPair[root]; contents ¬ NEW[ViewerTools.TiogaContentsRec ¬ [pair.contents, pair.formatting]]; }; }; WritePlainCR: PUBLIC PROC [root: TextNode.Ref] RETURNS[text: Rope.ROPE] = { forPlain: IO.STREAM ¬ IO.ROS[]; node: TextNode.Ref ¬ root; level: INTEGER ¬ 0; levelDelta: INTEGER; first: BOOL ¬ TRUE; pastFirstEmptyLine: BOOL ¬ FALSE; maxWidth: INT ¬ infiniteWidth; spaces: ROPE ~ IF indentWidth < 4 THEN " " ELSE " "; DO text: Tioga.Node; col, start, len: INT ¬ 0; [node, levelDelta] ¬ TextNode.Forward[node]; IF node=NIL THEN EXIT; IF ( level ¬ level+levelDelta ) = 1 THEN maxWidth ¬ infiniteWidth ELSE maxWidth ¬ lowerLevelWidth; text ¬ node; IF text = NIL THEN LOOP; IF first THEN first ¬ FALSE ELSE { IF pastFirstEmptyLine THEN SELECT text.format FROM $head, $body, $block, $item, $equation, $display, $indent => forPlain.PutRope["\r"]; ENDCASE => NULL; forPlain.PutRope["\r"]; }; len ¬ text.rope.Length[]; IF len = 0 THEN pastFirstEmptyLine ¬ TRUE; WHILE start < len DO end: INT ¬ -1; THROUGH [1..level) DO forPlain.PutRope[spaces]; ENDLOOP; -- output level-1 quads of spaces col ¬ (level-1)*indentWidth; IF maxWidth > col THEN { dlmIndex: INT ~ Rope.SkipTo[s: text.rope, pos: start, skip: "\r\l"]; lim: INT ~ IF pastFirstEmptyLine THEN MIN[dlmIndex, start+maxWidth-col] ELSE dlmIndex; end ¬ lim; IF (lim < dlmIndex) THEN { FOR end ¬ end, end-1 WHILE end > start AND NOT White[text.rope.Fetch[end]] DO NULL ENDLOOP; IF end=start THEN { end ¬ MIN[dlmIndex, Rope.SkipTo[s: text.rope, pos: start+1, skip: "\t "]] }; }; }; forPlain.PutRope[text.rope.Substr[start, end-start]]; FOR end ¬ end, end+1 WHILE end start ¬ start + 1; ENDCASE; }; ENDLOOP; ENDLOOP; text ¬ forPlain.RopeFromROS[]; forPlain.Close[]; }; infiniteWidth: INT ¬ LAST[INT]/2; --can't make too big (start+maxWidth-col) calc lowerLevelWidth: INT ¬ 80; indentWidth: INT ¬ 2; White: PROC [c: CHAR] RETURNS [BOOL] ~ INLINE {RETURN [c=' OR c='\t]}; END. ΰMailUtilsImpl.mesa Copyright Σ 1988, 1898, 1990, 1991, 1993 by Xerox Corporation. All rights reserved. Doug Terry, December 12, 1988 6:21:50 pm PST Wes Irish, December 21, 1988 9:49:34 pm PST Willie-sue, February 15, 1993 3:18 pm PST Utilities for manipulating mail messages, postmarks, recipients, etc. Generate a postmark from a message's time (when sent), machine (where posted or originated), and sender (who sent); if gmt is BasicTime.nullGMT then BasicTime.Now[] is used. check for Bogus (in 1993) dates in 2xxx Gets the time out of the message postmark. Returns TRUE if r has the form of a known MailBasics.Postmark. This was a XC character that contained a LF or CR in the low order bits. This should never happen! Κ 3•NewlineDelimiter –(cedarcode) style˜code•Mark outsideHeaderšΟb™Kšœ ΟeœI™TKšœ)Οk™,K™+K™)K™K™EK™—šŸ ˜ Kšœ ŸœŸœ˜1KšœŸœ˜KšœŸœ+˜8KšŸœ˜Kšœ ˜ K˜K˜ KšœŸœ ˜Kšœ˜Kšœ ˜ Kšœ˜K˜ Kšœ ˜ K˜K˜Kšœ Ÿœ#˜4—K˜šΡbln œŸœŸ˜KšŸœŸœ7˜[KšŸœ˜$KšœŸœŸœ ˜K˜KšŸœŸœŸœ˜KšŸœŸœŸœŸœ˜K˜šœŸœŸœ˜!KšœŸœ˜ Kšœ˜Kšœ˜K˜K˜Kšœ˜K˜—šΟnœŸœŸœŸœ ŸœŸœ ŸœŸœ˜fKšŸœŸœŸœ˜K˜š ŸœŸœŸœ6ŸœŸœŸ˜VKšœŸœ˜KšŸœ ŸœŸœŸœ˜OKšŸœŸœŸœ Ÿœ˜-KšŸœ˜—K˜K˜—šœŸœŸœŸœ˜8K˜—š ‘œŸœŸœŸœ Ÿœj˜₯KšŸœŸœŸœ˜KšœŸœ?˜]K˜K˜—š‘œŸœŸœŸœŸœŸœŸœŸœ˜[KšŸœŸœŸœ˜Kšœ ŸœŸœ˜Kš ŸœŸœŸœŸœŸœ˜4KšœŸœ˜ š ŸœŸœŸœŸœŸœŸ˜:š ŸœŸœŸœ6ŸœŸœŸ˜VKšœŸœ˜ KšŸœ)ŸœŸœ˜5K˜$Kšœ Ÿœ˜Kšœ Ÿœ˜KšŸœ˜—KšŸ˜—KšŸœ Ÿœ˜K˜K™—š‘œŸœŸœ˜2KšŸœŸœŸœ˜š ŸœŸœŸœ6ŸœŸœŸ˜XKšŸœ ŸœŸœ˜,KšŸœ'ŸœŸœŸœ˜9KšŸœ˜—K˜K˜—š‘œŸœŸœŸœŸœŸœŸœ˜EKšŸœŸœŸœ˜š ŸœŸœŸœ6ŸœŸœŸ˜XKšŸœŸœŸœ˜)KšŸœŸœŸœŸœ˜0KšŸœ˜—K˜K˜—š ‘œŸœŸœŸœŸœŸœ˜{Kšœ­™­KšœŸœ˜ KšœŸœ ˜ KšŸœŸœ˜4Kšœ&Οc˜AK™'˜1Kšœ2Ÿœ˜>—KšœŸœ3˜:K˜K™—š ‘œŸœŸœ!ŸœŸœ˜zKšœ*™*K•StartOfExpansion>[s1: ROPE, s2: ROPE, pos1: INT _ 0, case: BOOL _ TRUE]šœŸœJ˜RKšœ2Ÿœ˜KšœŸœ˜ KšœŸœ˜ KšŸœŸœŸœŸœ˜.KšŸœŸœŸœŸœ˜.K˜>Kšœ7ŸœŸœ˜IKšŸœ˜ K˜K˜—šΠbnœŸ œ!ŸœŸœ˜VKšŸœ˜šœŸœ Ÿ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ)˜)Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜3K˜3K˜3K˜7K˜7K˜9K˜7K˜7Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ%˜%Kšœ˜šŸœŸœŸœŸ˜KšœŸœŸœ/˜hKšœŸœŸœ/˜hKšœŸœŸœ)˜VKšœŸœŸœ+˜\KšœŸœŸœ+˜\KšŸœŸœ-˜:——K˜K˜—š‘œŸœŸœŸœŸœ Ÿœ Ÿœ˜\K˜+KšœŸœ˜ Kšœ Ÿœ˜šŸœŸœŸœ Ÿ˜KšŸœŸœ Ÿœ˜DK˜šŸœŸœ˜K˜'K˜ K˜K˜—Kš ŸœŸœŸœŸœ Ÿœ˜AKšŸœ˜—K–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šŸœ Ÿœ=˜LK˜K˜—š ‘œŸœŸœŸœ(Ÿœ˜lKšœŸœ˜KšœA˜AšŸœMŸœŸœŸ˜cKšœŸœ˜!–)[s: ROPE, pos: INT _ 0, skip: ROPE]šŸœŸœjŸœŸ˜–η[root: TextEdit.RefTextNode, dest: TextEdit.RefTextNode, char: CHAR, start: INT _ 0, len: INT _ 2147483647, inherit: BOOL _ TRUE, looks: TextLooks.Looks, charSet: TextEdit.CharSet _ 0, event: TextEdit.Event _ NIL]šŸœ.˜0šŸœ˜Kšœ]ŸœŸœ˜uKšœ˜—šŸœ˜K™cKšœ\ŸœŸœ˜tKšœ˜——KšŸœ˜—KšŸœ˜—˜K˜*Kšœ ŸœB˜PK˜—Kšœ˜K˜—š ‘ œŸœŸœŸœ Ÿœ˜KKš œ ŸœŸœŸœŸœ˜K˜KšœŸœ˜Kšœ Ÿœ˜KšœŸœŸœ˜KšœŸœŸœ˜!Kšœ Ÿœ˜Kš œŸœŸœŸœŸœ˜8šŸ˜K˜KšœŸœ˜K˜,KšŸœŸœŸœŸœ˜KšŸœ"ŸœŸœ˜bK˜ KšŸœŸœŸœŸœ˜šŸœŸœ ŸœŸœ˜"šŸœŸœŸœ Ÿ˜2KšœU˜UKšŸœŸœ˜—Kšœ˜Kšœ˜—K˜KšŸœ ŸœŸœ˜*šŸœ Ÿ˜KšœŸœ˜KšŸœ ŸœŸœ’!˜ZK˜šŸœŸœ˜Kšœ Ÿœ7˜DKš œŸœŸœŸœŸœŸœ ˜VK˜ šŸœŸœ˜KšŸœŸœ ŸœŸœŸœŸœŸœ˜[KšŸœ Ÿœ ŸœC˜`K˜—K˜—Kšœ5˜5Kš ŸœŸœ ŸœŸœŸœŸœ˜SKšŸœ ŸœŸœ˜.K˜ šŸœ Ÿœ˜Kšœ˜šŸœŸ˜"K˜KšŸœ˜—K˜—KšŸœ˜—KšŸœ˜—K˜Kšœ˜K˜K˜—KšœŸœŸœŸœ’.˜RKšœŸœ˜šœ Ÿœ˜K˜—š‘œŸœŸœŸœŸœŸœŸœŸœ ˜GK˜——K˜KšŸœ˜—…—"Ύ2Ρ