DIRECTORY Commander, InputFocus USING [GetInputFocus], IO, MailAnswer USING [MakeHeader], MailBasics, MailParse, Menus, PFS, Process USING [Detach], Rope, RuntimeError USING [BoundsFault], SendMailParseMsg, SendMailInternal, SendMailOps, TEditInputOps USING [DoFindPlaceholders], Tioga, TiogaOps USING [ Ref, Location, ViewerDoc, CallWithLocks, CancelSelection, CaretBefore, Delete, FindText, GetSelection, InsertRope, SelectDocument, SelectionRoot, SelectPoint, SetSelection, SetSelectionLooks, ToPrimary], ViewerOps, ViewerClasses, ViewerEvents USING [EventProc, RegisterEventProc, UnRegisterEventProc], ViewerTools; SendMailOpsImpl: CEDAR MONITOR IMPORTS Commander, IO, PFS, Process, Rope, RuntimeError, MailAnswer, MailParse, SendMailInternal, SendMailOps, TEditInputOps, TiogaOps, InputFocus, Menus, ViewerEvents, ViewerOps, ViewerTools EXPORTS SendMailInternal, SendMailOps, SendMailParseMsg SHARES ViewerClasses = BEGIN OPEN SendMailOps, SendMailInternal, SendMailParseMsg; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; STREAM: TYPE = IO.STREAM; TiogaContents: TYPE = ViewerTools.TiogaContents; viewerStart: ViewerTools.SelPos = NEW[ViewerTools.SelPosRec ¬ [0, 0]]; nullIndex: INT = LAST[INT]; messageParseArray: PUBLIC ARRAY MessageFieldIndex OF MessageInfo ¬ [ ["Reply-to", simpleRope], -- this is really wrong, a special case for now ["Sender", simpleRope], ["From", simpleRope], ["To", rNameList], ["cc", rNameList], ["c", rNameList], ["bcc", rNameList], ["Date", simpleRope], ["Subject", simpleRope], ["Categories", rCatList], ["In-reply-to", simpleRope], ["VoiceFileID", simpleRope] ]; sendMailDebug: BOOL ¬ FALSE; Answer: PUBLIC PROC[msgHeaders: ROPE, who: Viewer ¬ NIL, which: ATOM] RETURNS [v: Viewer] = { notOk: BOOL; errorIndex: INT; answer: ROPE; answerForm: SendMailOps.Form; thisUser: MailBasics.RName ¬ [$none, NIL]; AnswerGetChar: PROC[pos: INT] RETURNS[CHAR] = { ch: CHAR ~ msgHeaders.Fetch[pos]; RETURN[IF ch = '\l THEN '\r ELSE ch]; }; IF ( which # NIL ) THEN { IF userRNameList = NIL THEN DoUserNameAndRegistry[]; IF userRNameList = NIL THEN SenderReport["\n*** You have not logged in; answer form will not be completely correct\n"]; FOR rL: MailBasics.RNameList ¬ userRNameList, rL.rest UNTIL rL = NIL DO IF ( which # rL.first.ns ) THEN LOOP; thisUser ¬ rL.first; EXIT; ENDLOOP; }; IF sendMailDebug THEN { SenderReport["\n*** about to answer:\n%g\n*****\n", [rope[msgHeaders]] ]; }; [notOk, answer, errorIndex] ¬ MailAnswer.MakeHeader[which, AnswerGetChar, msgHeaders.Length[], thisUser]; IF sendMailDebug THEN { SenderReport["\n*** got the answer:\n%g\n*****\n", [rope[answer]] ]; }; IF notOk THEN { start, end: INT ¬ errorIndex; BEGIN ENABLE RuntimeError.BoundsFault => {start ¬ 0; end ¬ 1; CONTINUE}; IF start = nullIndex THEN start ¬ 0 ELSE {UNTIL msgHeaders.Fetch[start] = '\r DO start ¬ start - 1; ENDLOOP; start ¬ start + 1}; IF end = nullIndex THEN end ¬ start + 1 ELSE {UNTIL msgHeaders.Fetch[end] = '\r DO end ¬ end + 1; ENDLOOP; end ¬ end - 1}; END; IF who # NIL THEN ShowErrorFeedback[who, start, end]; SenderReport[ "\nSyntax error in header line \"%g\"", [rope[msgHeaders.Substr[start, end-start+1] ]] ]; IF answer.Length[] = 0 THEN RETURN; SenderReport["\n*****Partial answer has been generated\n"]; }; answerForm ¬ NEW[ SendMailOps.FormRec ¬ [formText: answerText, fields: ParseAnswerHeader[answer] ] ]; v ¬ BuildSendViewer[TRUE, FALSE, answerForm, who, alwaysNewSender].v; ClearFileAssoc[v]; GrabFocus[v]; }; ParseAnswerHeader: PROC[header: ROPE] RETURNS[fields: LIST OF ROPE] = { HeaderLines: ARRAY[0..3] OF Rope.ROPE; startPos: INT ¬ Rope.Find[header, ": ", 0]; endOfLine: INT ¬ Rope.Find[header, "\r", startPos]; FOR i: INT IN [0..3] DO HeaderLines[i] ¬ Rope.Substr[header, startPos+2, endOfLine-startPos-2]; startPos ¬ Rope.Find[header, ": ", endOfLine]; IF startPos = -1 THEN EXIT ELSE endOfLine ¬ Rope.Find[header, "\r", startPos] ENDLOOP; RETURN[ LIST[ HeaderLines[0], HeaderLines[1], HeaderLines[2], HeaderLines[3] ] ] }; smallBoldLooks: Tioga.Looks ¬ ALL[FALSE]; Forward: PUBLIC PROC[msg: Viewer, who: Viewer _ NIL] RETURNS[v: Viewer] = { forwardForm: SendMailOps.Form = NEW[SendMailOps.FormRec _ [formText: forwardText, fields: NIL]]; pstart, pend: TiogaOps.Location; root: TiogaOps.Ref; found: BOOL; v _ BuildSendViewer[TRUE, FALSE, forwardForm, who, alwaysNewSender].v; ClearFileAssoc[v]; TiogaOps.SelectDocument[v]; root _ TiogaOps.SelectionRoot[]; pstart _ TiogaOps.Location[ root, 0 ]; TiogaOps.SelectPoint[ v, pstart ]; found _ TiogaOps.FindText[v, "MessageHeader"]; IF found THEN { [ , pstart, pend, , , ] _ TiogaOps.GetSelection[]; TiogaOps.SetSelection[viewer: v, start: pstart, end: pend, level: word, pendingDelete: TRUE]; TiogaOps.InsertRope[ msg.name ] }; found _ TiogaOps.FindText[v, "ForwardedMessage"]; IF NOT found THEN RETURN; [ , pstart, pend, , , ] _ TiogaOps.GetSelection[]; TiogaOps.SetSelection[viewer: v, start: pstart, end: pend, level: node, pendingDelete: TRUE ]; TiogaOps.SelectDocument[ viewer: msg, which: secondary ]; TiogaOps.ToPrimary[]; UnsetNewVersion[v]; GrabFocus[v] }; original: ROPE ~ "Original-"; originally: ROPE ~ "Originally-"; ReSend: PUBLIC PROC[msg: Viewer, who: Viewer ¬ NIL] RETURNS[v: ViewerClasses.Viewer] = { reSendForm: SendMailOps.Form = NEW[SendMailOps.FormRec ¬ [formText: SendMailOps.GetCRTiogaContents[msg], fields: NIL]]; AddPrefix: PROC[what, prefix: ROPE] ~ { IF TiogaOps.FindText[viewer: v, rope: what, whichDir: anywhere, case: FALSE] THEN { TiogaOps.CaretBefore[]; TiogaOps.SetSelectionLooks[]; TiogaOps.InsertRope[prefix]; }; }; v ¬ BuildSendViewer[TRUE, FALSE, reSendForm, who, alwaysNewSender].v; ClearFileAssoc[v]; TiogaOps.SelectDocument[v]; AddPrefix["Date:", original]; AddPrefix["Sender:", original]; AddPrefix["From:", originally]; AddPrefix["Message-ID:", original]; UnsetNewVersion[v]; }; ClearFileAssoc: PUBLIC PROC[v: Viewer] = { IF v.file # NIL THEN v.file ¬ NIL; v.name ¬ sendCaption; ViewerOps.PaintViewer[v, caption]; }; SenderNewVersion: PUBLIC PROC[viewer: Viewer] = { OPEN Menus; menu: Menus.Menu = viewer.menu; firstForm: Menus.MenuEntry ¬ NIL; getEntry: Menus.MenuEntry ¬ Menus.FindEntry[menu, "Get"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; getEntry ¬ Menus.FindEntry[menu, "Default"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; getEntry ¬ Menus.FindEntry[menu, "PrevMsg"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; FOR i: Menus.MenuLine IN [1..5) DO thisLine: Menus.MenuEntry = Menus.GetLine[menu, i]; IF thisLine = NIL THEN EXIT; IF firstForm = NIL THEN EXIT; IF Rope.Equal[thisLine.name, firstForm.name] THEN FOR entry: MenuEntry ¬ thisLine, entry.link UNTIL entry = NIL DO SetGuarded[entry, TRUE] ENDLOOP; ENDLOOP; ViewerOps.PaintViewer[viewer, menu]; -- show as guarded viewer.newVersion ¬ TRUE; }; UnsetNewVersion: PUBLIC PROC[viewer: Viewer] = { OPEN Menus; menu: Menu = viewer.menu; firstForm: Menus.MenuEntry ¬ NIL; getEntry: Menus.MenuEntry ¬ Menus.FindEntry[menu, "Get"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; getEntry ¬ Menus.FindEntry[menu, "Default"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; getEntry ¬ Menus.FindEntry[menu, "PrevMsg"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; FOR i: Menus.MenuLine IN [1..5) DO thisLine: Menus.MenuEntry = Menus.GetLine[menu, i]; IF thisLine = NIL THEN EXIT; IF firstForm = NIL THEN EXIT; IF Rope.Equal[thisLine.name, firstForm.name] THEN FOR entry: MenuEntry ¬ thisLine, entry.link UNTIL entry = NIL DO SetGuarded[entry, FALSE] ENDLOOP; ENDLOOP; ViewerOps.PaintViewer[viewer, menu]; -- show as unguarded viewer.newVersion ¬ FALSE; ViewerOps.PaintViewer[viewer, caption]; }; TiogaTextFromStrm: PUBLIC PROC [strm: IO.STREAM, startPos: INT ¬ 0, len: INT ¬ LAST[INT]] RETURNS [contents: TiogaContents] = { ENABLE IO.EndOfStream => GOTO TooShort; fulltext: ROPE ¬ RopeFromStream[strm, startPos, len]; formatPos: INT ¬ Rope.Index[fulltext, 0, "\000\000"]; contents ¬ NEW[ViewerTools.TiogaContentsRec]; contents.contents ¬ Rope.Substr[fulltext, 0, formatPos]; IF formatPos < Rope.Length[fulltext] THEN contents.formatting ¬ Rope.Substr[fulltext, formatPos]; EXITS TooShort => RETURN[NIL]; }; RopeFromStream: PUBLIC PROC [strm: STREAM, startPos, len: INT] RETURNS [contents: ROPE] = { rem: INT ¬ IO.GetLength[strm] - startPos; IF rem < len THEN len ¬ rem; IF len < 2048 THEN TRUSTED { nat: NAT ¬ len; text: Rope.Text ¬ Rope.NewText[nat]; IO.SetIndex[strm, startPos]; [] ¬ IO.GetBlock[strm, LOOPHOLE[text], 0, nat]; RETURN [text]; }; rem ¬ len/2; len ¬ len - rem; contents ¬ RopeFromStream[strm, startPos, len]; contents ¬ Rope.Concat[contents, RopeFromStream[strm, startPos+len, rem] ]; }; GetFieldBody: PUBLIC PROC[text, fieldName: ROPE] RETURNS[fieldBody: ROPE] = { OPEN MailParse; mPos: INT ¬ 0; lastCharPos: INT ¬ text.Length[]; pH: MailParse.ParseHandle ¬ MailParse.InitializeParse[]; NextChar: PROC[] RETURNS [ch: CHAR] = { IF mPos > lastCharPos THEN ch ¬ endOfInput ELSE ch ¬ text.Fetch[mPos]; mPos ¬ mPos + 1; }; BEGIN field: ROPE ¬ NIL; DO found, fieldNotRecognized: BOOL ¬ TRUE; field ¬ MailParse.GetFieldName[pH, NextChar ! ParseError => GOTO parseErrorExit]; IF field = NIL THEN EXIT; IF Rope.Equal[fieldName, field, FALSE] THEN -- ignore case { fieldBody ¬ MailParse.GetFieldBody[pH, NextChar]; EXIT} ELSE [] ¬ MailParse.GetFieldBody[pH, NextChar, TRUE]; ENDLOOP; MailParse.FinalizeParse[pH]; EXITS parseErrorExit => { MailParse.FinalizeParse[pH]; RETURN[NIL]}; END; }; GetRecipients: PUBLIC PROC[sender: Viewer, transport: ATOM ¬ NIL] RETURNS[rList: MailBasics.RNameList, parseError: BOOL] = { text: ROPE; status: SendParseStatus; sPos, mPos: INT; TRUSTED {text ¬ SendMailOps.CreateRopeForTextNode[LOOPHOLE [TiogaOps.ViewerDoc[sender]]]}; [status, sPos, mPos, rList] ¬ Parse[text, transport]; IF (status # ok) AND (status # includesPublicDL) THEN BEGIN SELECT status FROM fieldNotAllowed => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport[" %g field is not allowed\n", [rope[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]]] ] } ELSE SenderReport[" field at pos %g is not allowed\n", [integer[sPos]] ]; syntaxError => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport["\nSyntax error on line beginning with %g", [rope[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]] ] ]; } ELSE SenderReport["..... Syntax error at position %g ", [integer[sPos]] ]; includesPrivateDL => SenderReport[" Private dl's are not yet implemented\n"]; ENDCASE => ERROR; ViewerOps.BlinkIcon[sender, IF sender.iconic THEN 0 ELSE 1]; RETURN[NIL, TRUE] END; RETURN[rList, FALSE] }; Parse: PUBLIC PROC[text: ROPE, transport: ATOM] RETURNS[status: SendParseStatus, sPos, mPos: INT, rList: MailBasics.RNameList] = { smr: SendingRec ¬ NEW[SendMsgRecObject]; smr.fullText ¬ text; [status, sPos, mPos] ¬ ParseText[smr, transport]; IF ( status = fieldNotAllowed ) OR ( status = syntaxError ) THEN RETURN; IF smr.cc = NIL THEN { rList ¬ smr.to; RETURN}; IF smr.to = NIL THEN { rList ¬ smr.cc; RETURN}; BEGIN rL: MailBasics.RNameList ¬ smr.cc; -- cc usually shorter?? rList ¬ smr.cc; UNTIL rL.rest = NIL DO rL ¬ rL.rest; ENDLOOP; rL.rest ¬ smr.to; END; }; ParseHeadersFromRope: PUBLIC PROC[headers: ROPE, proc: ParseProc] RETURNS[msgHeaders: MsgHeaders] = { OPEN MailParse; mPos: INT ¬ 0; len: INT ¬ headers.Length[]; pH: MailParse.ParseHandle ¬ MailParse.InitializeParse[]; NextChar: PROC[] RETURNS [ch: CHAR] = { IF mPos >= len THEN ch ¬ endOfInput ELSE ch ¬ headers.Fetch[mPos]; mPos ¬ mPos + 1; }; msgHeaders ¬ NIL; IF headers.Fetch[0] = '\r THEN mPos ¬ 1; -- ignore initial CR (tioga formatting nonsense) { ENABLE ParseError => GOTO parseErrorExit; fieldName: ROPE ¬ NIL; wantThisField, continue: BOOL ¬ TRUE; DO fieldName ¬ MailParse.GetFieldName[pH, NextChar]; IF fieldName = NIL THEN EXIT; IF proc # NIL THEN [wantThisField, continue] ¬ proc[fieldName]; IF wantThisField THEN msgHeaders ¬ CONS[[fieldName, MailParse.GetFieldBody[pH, NextChar]], msgHeaders] ELSE [] ¬ MailParse.GetFieldBody[pH, NextChar, TRUE]; IF ~continue THEN EXIT; ENDLOOP; MailParse.FinalizeParse[pH]; EXITS parseErrorExit => { MailParse.FinalizeParse[pH]; RETURN[msgHeaders]}; }; }; ParseMsgFromStream: PUBLIC PROC[strm: IO.STREAM, len: INT, proc: ParseProc] RETURNS[msgHeaders: MsgHeaders] = { OPEN MailParse; mPos: INT ¬ 0; pH: MailParse.ParseHandle ¬ MailParse.InitializeParse[]; NextChar: PROC[] RETURNS [ch: CHAR] = { IF mPos > len THEN ch ¬ endOfInput ELSE ch ¬ strm.GetChar[ ! IO.EndOfStream => { mPos ¬ len; ch ¬ endOfInput; CONTINUE } ]; mPos ¬ mPos + 1; }; msgHeaders ¬ NIL; IF strm.PeekChar[] = '\r THEN { -- ignore initial CR (tioga formatting nonsense) [] ¬ strm.GetChar[]; mPos ¬ 1; }; { ENABLE ParseError => GOTO parseErrorExit; fieldName: ROPE ¬ NIL; wantThisField, continue: BOOL ¬ TRUE; DO fieldName ¬ MailParse.GetFieldName[pH, NextChar]; IF fieldName = NIL THEN EXIT; IF proc # NIL THEN [wantThisField, continue] ¬ proc[fieldName]; IF wantThisField THEN msgHeaders ¬ CONS[[fieldName, MailParse.GetFieldBody[pH, NextChar]], msgHeaders] ELSE [] ¬ MailParse.GetFieldBody[pH, NextChar, TRUE]; IF ~continue THEN EXIT; ENDLOOP; MailParse.FinalizeParse[pH]; EXITS parseErrorExit => { MailParse.FinalizeParse[pH]; RETURN[msgHeaders]}; }; }; GetSendForm: PUBLIC PROC[fileName: ROPE] RETURNS[text: ViewerTools.TiogaContents] = { s: IO.STREAM; so: PFS.StreamOptions ¬ PFS.defaultStreamOptions; so[includeFormatting] ¬ TRUE; s ¬ PFS.StreamOpen[fileName: PFS.PathFromRope[fileName], streamOptions: so ! PFS.Error => { s ¬ NIL; CONTINUE }]; IF s # NIL THEN { text ¬ TiogaTextFromStrm[s ! PFS.Error => { text ¬ NIL; CONTINUE }]; s.Close[ ! PFS.Error => { s ¬ NIL; CONTINUE }] }; }; InternalDisplayTioga: PUBLIC PROC[ senderInfo: SenderInfo, tc: TiogaContents, grab: BOOL] = { senderV: Viewer ¬ senderInfo.senderV; iHadFocus: BOOL ¬ InputFocus.GetInputFocus[].owner = senderV; IF senderV.link # NIL THEN InternalDestroySplits[senderV]; IF TiogaOps.GetSelection[feedback].viewer = senderV THEN TiogaOps.CancelSelection[feedback]; ViewerTools.SetTiogaContents[senderV, tc]; -- test if I had the focus & no-one else has it now IF grab AND iHadFocus AND InputFocus.GetInputFocus[].owner = NIL THEN { ViewerTools.SetSelection[senderV, viewerStart]; [] ¬ TEditInputOps.DoFindPlaceholders[next: TRUE, gotoend: FALSE] }; UnsetNewVersion[senderV]; senderInfo.successfullySent ¬ FALSE; }; InsertForm: PUBLIC PROC[ sender: SenderInfo, form: SendMailOps.Form, force: BOOL ] = { senderV: Viewer = sender.senderV; whoHasIt: Viewer; IF senderV.iconic THEN { ViewerOps.AddProp[senderV, $SendMailOpsForm, form]; sender.openEvent ¬ ViewerEvents.RegisterEventProc[ proc: OpenSendViewer, event: open, filter: senderV, before: FALSE]; UnsetNewVersion[senderV]; -- not strictly true but want to reuse RETURN }; -- first stuff the text of the form into the Viewer InternalDisplayTioga[sender, form.formText, FALSE]; IF force OR ((whoHasIt ¬ InputFocus.GetInputFocus[].owner) = NIL) OR (whoHasIt = senderV) THEN DoPlaceHolders[senderV, form.fields] ELSE { ViewerOps.AddProp[senderV, $SendMailOpsFields, form.fields]; sender.focusEvent ¬ ViewerEvents.RegisterEventProc[ proc: SetFocusInSendViewer, event: setInputFocus, filter: senderV, before: FALSE]; UnsetNewVersion[senderV]; -- not strictly true but want to reuse }; }; OpenSendViewer: ViewerEvents.EventProc = { OPEN Menus; senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; form: SendMailOps.Form; ra: REF ANY; IF senderInfo = NIL THEN RETURN[FALSE]; IF (ra ¬ ViewerOps.FetchProp[viewer, $SendMailOpsForm]) = NIL THEN RETURN[FALSE]; form ¬ NARROW[ra]; ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open]; senderInfo.openEvent ¬ NIL; TRUSTED { Process.Detach[FORK EntryInsertForm[senderInfo, form]]}; RETURN[FALSE]; }; SetFocusInSendViewer: ViewerEvents.EventProc = { OPEN Menus; senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; fieldsList: LIST OF ROPE; ra: REF ANY; IF senderInfo = NIL THEN RETURN[FALSE]; IF (ra ¬ ViewerOps.FetchProp[viewer, $SendMailOpsFields]) = NIL THEN RETURN[FALSE]; fieldsList ¬ NARROW[ra]; ViewerEvents.UnRegisterEventProc[senderInfo.focusEvent, setInputFocus]; senderInfo.focusEvent ¬ NIL; TRUSTED { Process.Detach[FORK EntryPlaceHolders[senderInfo, fieldsList]]}; RETURN[FALSE]; }; DoPlaceHolders: PUBLIC PROC[senderV: Viewer, fieldsList: LIST OF ROPE] = { AddFields: PROC[ root: TiogaOps.Ref ] = { -- now try to find all of the placeholders in the text and match them with the entries in the fields list FOR rl: LIST OF ROPE ¬ fieldsList, rl.rest UNTIL rl = NIL DO field: ROPE = rl.first; IF NOT TEditInputOps.DoFindPlaceholders[next: TRUE, gotoend: FALSE].found THEN EXIT; IF field = NIL THEN LOOP; TiogaOps.Delete[]; TiogaOps.InsertRope[field] ENDLOOP }; IF senderV.destroyed OR senderV.iconic THEN RETURN; -- oops ViewerTools.SetSelection[ senderV, viewerStart]; TiogaOps.CallWithLocks[ AddFields ]; UnsetNewVersion[senderV]; ViewerTools.SetSelection[ senderV, viewerStart]; [] ¬ TEditInputOps.DoFindPlaceholders[next: TRUE, gotoend: FALSE]; }; Init: PROC ~ { smallBoldLooks['b] ¬ smallBoldLooks['s] ¬ TRUE; }; SendMailAnswerDebug: Commander.CommandProc = { sendMailDebug ¬ TRUE }; SendMailAnswerUndebug: Commander.CommandProc = { sendMailDebug ¬ FALSE }; Commander.Register["SendMailAnswerDebug", SendMailAnswerDebug]; Commander.Register["SendMailAnswerUndebug", SendMailAnswerUndebug]; END. ˆ SendMailOpsImpl.mesa Copyright Σ 1984, 1985, 1986, 1989, 1992 by Xerox Corporation. All rights reserved. Willie-Sue, December 21, 1989 4:31:45 pm PST Contents: Implementations for SendMailSendOps Created by Willie-Sue, December 13, 1983 Willie-s, July 30, 1992 12:02 pm PDT ************************************************************************ firstForm: Menus.MenuEntry = Menus.GetLine[formsMenu, 0]; firstForm: Menus.MenuEntry = Menus.GetLine[formsMenu, 0]; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * returns NIL IF endOfStream encountered during read Reads arbitrary length ROPE from a stream. If some caller catches the error, then the caller can diagnose it. Otherwise we do something more or less reasonable here. Short enough to make in one piece The rope is too large to make in one piece, so we divide the length in two and recursviely call ourselves. This gets at least 1024 characters in each piece in a balanced binary tree, so the recursion is quite acceptable even for large messages. Do this first to ensure that the evaluation order is OK. expects text to be the text of a message; parses the header fields looking for a field named fieldName and returns the value of the fieldBody, or NIL if no such fieldName is found expects sender to contain a message to be sent; will parse the headers and return a list of all the recipients of the message; if an error occurs while parsing the viewer, parseError=TRUE is returned, and the viewer will have the offending field underlined. need to merge the two lists proc is called for each fieldName encountered in the headers; if proc = NIL then all fields are returned expects strm to be the beginning of a message; parses the header fields looking for fields, calling proc with each fieldName as found; if proc=NIL then return all fields procs moved out of SendMailControlImpl in the great gfi hackery really an INTERNAL PROC Κo–(cedarcode) style•NewlineDelimiter ™codešΟb™Kšœ ΟeœI™TK™,K™Kšœ-™-Kšœ(™(K™$—K˜šΟk ˜ K˜ Kšœ Ÿœ˜!KšŸœ˜Kšœ Ÿœ˜K˜ K˜ K˜KšŸœ˜KšœŸœ ˜K˜Kšœ Ÿœ˜!Kšœ˜K˜Kšœ ˜ KšœŸœ˜)K˜Kšœ ŸœΞ˜άK˜ K˜Kšœ Ÿœ5˜GK˜ K˜—šΟnœŸœŸ˜šŸ˜Kšœ ŸœŸœ˜0K˜Kšœ˜K˜K˜7—šŸ˜Kšœ/˜/—KšŸœ˜—˜KšŸœŸœ1˜;—K˜KšœH™HK˜KšŸœŸœŸœ˜KšœŸœ˜$KšŸœŸœŸœŸœ˜KšœŸœ˜0Kšœ"Ÿœ!˜FK˜Kšœ ŸœŸœŸœ˜K˜šœŸœŸœŸœ˜DKšœΟc/˜KK˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜KšœŸœŸœ˜—˜š œŸœŸœ ŸœŸœ ŸœŸœ˜]KšœŸœ˜ Kšœ Ÿœ˜KšœŸœ˜ K˜Kšœ%Ÿœ˜*š   œŸœŸœŸœŸœ˜/KšœŸœ˜!KšŸœŸœ ŸœŸœ˜%Kšœ˜—K˜šŸœ ŸœŸœ˜KšŸœŸœŸœ˜4šŸœŸœŸ˜Kšœ[˜[—šŸœ3ŸœŸœŸ˜GKšŸœŸœŸœ˜%K˜KšŸœ˜KšŸœ˜—K˜K˜—šŸœŸœ˜KšœI˜IK˜K˜—K˜iK˜šŸœŸœ˜KšœD˜DK˜K˜—šŸœŸœ˜Kšœ Ÿœ ˜šŸœŸœ2Ÿœ˜HšŸœŸœ ˜#KšŸ˜—šœŸœŸœŸœ˜CK˜—šŸœŸœ˜'KšŸ˜—šœŸœŸœŸœ˜=K˜—KšŸœ˜—KšŸœŸœŸœ$˜5K˜K˜gKšŸœŸœŸœ˜#Kšœ;˜;Kšœ˜—K˜Kšœ ŸœU˜eKšœŸœŸœ'˜FK˜K˜ Kšœ˜K˜—š œŸœ ŸœŸœ ŸœŸœŸœ˜GKšœ ŸœŸœŸœ˜&Kšœ Ÿœ˜+Kšœ Ÿœ%˜3šŸœŸœŸœŸ˜K˜GK˜.KšŸœŸœŸœŸœ.˜M—KšŸœ˜KšŸœŸœD˜PKšœ˜K˜—šœŸœŸœ˜)K˜—š  œŸœŸœŸœœŸœœ˜KKšœ Ÿœ7Ÿœ˜`K˜ K˜KšœŸœ˜ KšœŸœŸœ'˜FK˜K˜K˜ K˜&K˜"K˜.šŸœŸœ˜K˜2Kšœ;œŸœ˜]K˜K˜—K˜1KšŸœŸœŸœŸœ˜K˜2KšœWŸœ˜^K˜9K˜K˜K˜ Kšœ˜K˜—Kšœ Ÿœ˜šœ Ÿœ˜!K˜—š œŸœŸœŸœ˜3KšœŸœ˜&šœ˜šŸœ˜Kšœ9Ÿœ˜?——š  œŸœŸœ˜'šŸœDŸœŸœ˜SKšœ˜Kšœ˜Kšœ˜K˜—K˜—KšœŸœŸœ&˜EK˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ#˜#K˜K˜K˜—š œŸœŸœ˜*KšŸœ ŸœŸœ Ÿœ˜"K˜K˜"Kšœ˜K˜—š œŸœŸœ˜1KšŸœ˜ K˜K™9KšœŸœ˜!K˜K˜9KšŸœ ŸœŸœŸœ˜8K˜,KšŸœ ŸœŸœŸœ˜8K˜,KšŸœ ŸœŸœŸœ˜8K˜šŸœŸœŸ˜"K˜3KšŸœ ŸœŸœŸœ˜KšŸœ ŸœŸœŸœ˜KšŸœ+Ÿ˜1šŸœ)Ÿœ ŸœŸ˜@KšœŸœ˜KšŸœ˜—KšŸœ˜—Kšœ&‘˜8KšœŸœ˜Kšœ˜K˜—š œŸœŸœ˜0KšŸœ˜ K˜KšœŸœ˜!K™9K˜K˜9KšŸœ ŸœŸœŸœ˜9K˜,KšŸœ ŸœŸœŸœ˜9K˜,KšŸœ ŸœŸœŸœ˜9K˜šŸœŸœŸ˜"K˜3KšŸœ ŸœŸœŸœ˜KšŸœ ŸœŸœŸœ˜KšŸœ+Ÿ˜1šŸœ)Ÿœ ŸœŸ˜@KšœŸœ˜KšŸœ˜—KšŸœ˜—Kšœ&‘˜:KšœŸœ˜K˜'Kšœ˜K˜——KšœE™E˜š œŸœŸœŸœŸœ Ÿœ ŸœŸœŸœŸœ˜Kšœ2™2KšŸœŸœŸœ ˜'Kšœ ŸœŸœ$˜5Kšœ Ÿœ'˜5K˜Kšœ Ÿœ˜-K˜K˜8šŸœ#Ÿ˜)K˜8—K˜KšŸœ ŸœŸœ˜Kšœ˜K˜—š œŸœŸœŸœŸœŸœ Ÿœ˜[KšœŸœŒ™§K˜KšœŸœŸœ˜)KšŸœ Ÿœ ˜šŸœ ŸœŸœ˜Kšœ!™!KšœŸœ˜K˜$KšŸœ˜KšœŸœŸœ˜/KšŸœ˜K˜—Kšœυ™υK˜ K˜˜/Kšœ8™8—K˜KKšœ˜K˜—š   œŸ œŸœŸœ Ÿœ˜MKšœ³™³šŸœ ˜KšœŸœ˜Kšœ Ÿœ˜!K˜8K˜Kš œŸœŸœŸœ˜%šœŸœŸœŸœ˜HK˜—K˜K˜KšŸ˜KšœŸœŸœ˜šŸ˜KšœŸœŸœ˜'Kšœ<Ÿœ˜QKšŸœ ŸœŸœŸœ˜K˜šŸœŸœŸœ‘˜:šœ4Ÿœ˜9KšŸœ+Ÿœ˜5———KšŸœ˜K˜Kšœ˜šŸ˜Kšœ1ŸœŸœ˜>—KšŸœ˜—Kšœ˜K˜—š  œŸœŸœŸœŸœŸœ*Ÿœ˜|Kšœ‚™‚KšœŸœ˜ K˜Kšœ Ÿœ˜šŸ˜Kšœ*Ÿœ ˜R—K˜5KšŸœŸœŸ˜5šŸ˜šŸœŸ˜šœ˜šŸœ Ÿœ˜šœ&˜&KšœCŸœ˜a—Kšœ˜—KšŸœF˜J—K˜šœŸœ Ÿœ˜$Kšœ&˜&šœQŸœ˜qKšœ˜—KšŸœG˜K—K˜KšœM˜MKšŸœŸœ˜—KšœŸœŸœŸœ˜