<> <> <> <<>> <> <> DIRECTORY DefaultRemoteNames USING [Get], GVAnswer USING [MakeHeader], GVMailParse, Menus, IO, Rope, RuntimeError USING [BoundsFault], TiogaOps USING [ Pattern, Ref, Location, ViewerDoc, CreateSimplePattern, GetSelection, InsertRope, SelectDocument, SelectPoint, SelectionRoot, SelectionSearch, SetSelection, ToPrimary], ViewerOps, ViewerClasses, ViewerTools, WalnutDocumentRope, WalnutParseMsg USING [SendParseStatus, Parse], WalnutSendInternal, WalnutSendOps; WalnutSendOpsImpl: CEDAR MONITOR IMPORTS DefaultRemoteNames, IO, Rope, RuntimeError, GVAnswer, GVMailParse, Menus, TiogaOps, ViewerOps, WalnutDocumentRope, WalnutParseMsg, WalnutSendInternal, WalnutSendOps EXPORTS WalnutSendInternal, WalnutSendOps SHARES Menus = BEGIN OPEN WalnutSendOps, WalnutSendInternal; <<************************************************************************>> ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; STREAM: TYPE = IO.STREAM; TiogaContents: TYPE = ViewerTools.TiogaContents; nullIndex: INT = LAST[INT]; defaultRegistry: PUBLIC ROPE _ DefaultRemoteNames.Get[].registry; Answer: PUBLIC PROC[msgHeaders: ROPE, who: Viewer_ NIL] RETURNS [v: Viewer] = { notOk: BOOL; errorIndex: INT; answer: ROPE; answerForm: WalnutSendOps.Form; AnswerGetChar: PROC[pos: INT] RETURNS[CHAR] = {RETURN[msgHeaders.Fetch[pos]]}; [notOk, answer, errorIndex]_ GVAnswer.MakeHeader[AnswerGetChar, msgHeaders.Length[], simpleUserName, defaultRegistry]; 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] = '\n DO start_ start - 1; ENDLOOP; start_ start + 1}; IF end = nullIndex THEN end_ start + 1 ELSE {UNTIL msgHeaders.Fetch[end] = '\n DO end_ end + 1; ENDLOOP; end_ end - 1}; END; IF who # NIL THEN ShowErrorFeedback[who, start, end]; SenderReport[IO.PutFR[ "\nSyntax error in line starting at pos %g (in message being answered)", IO.int[start]]]; RETURN }; answerForm _ NEW[ WalnutSendOps.FormRec _ [formText: answerText, fields: ParseAnswerHeader[answer] ] ]; v_ BuildSendViewer[TRUE, FALSE, answerForm, who]; 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, "\n", 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, "\n", startPos] ENDLOOP; RETURN[ LIST[ HeaderLines[0], HeaderLines[1], HeaderLines[2], HeaderLines[3] ] ] }; Forward: PUBLIC PROC[msg: Viewer, who: Viewer_ NIL] RETURNS[v: Viewer] = TRUSTED { forwardForm: WalnutSendOps.Form = NEW[WalnutSendOps.FormRec _ [formText: forwardText, fields: NIL]]; messagePattern: TiogaOps.Pattern = TiogaOps.CreateSimplePattern["ForwardedMessage"]; headerPattern: TiogaOps.Pattern = TiogaOps.CreateSimplePattern["MessageHeader"]; pstart, pend: TiogaOps.Location; root: TiogaOps.Ref; found: BOOL; v_ BuildSendViewer[TRUE, FALSE, forwardForm, who]; ClearFileAssoc[v]; TiogaOps.SelectDocument[v]; root _ TiogaOps.SelectionRoot[]; pstart _ TiogaOps.Location[ root, 0 ]; TiogaOps.SelectPoint[ v, pstart ]; found _ TiogaOps.SelectionSearch[pattern: headerPattern]; 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.SelectionSearch[pattern: messagePattern]; 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] }; 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 = Menus.GetLine[formsMenu, 0]; 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 = Menus.GetLine[formsMenu, 0]; 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 GVMailParse; mPos: INT_ 0; lastCharPos: INT_ text.Length[]; pH: GVMailParse.ParseHandle_ GVMailParse.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, found]_ GVMailParse.GetFieldName[pH, NextChar ! ParseError => GOTO parseErrorExit]; IF ~found THEN EXIT; IF Rope.Equal[fieldName, field, FALSE] THEN -- ignore case { fieldBody_ GVMailParse.GetFieldBody[pH, NextChar]; EXIT} ELSE []_ GVMailParse.GetFieldBody[pH, NextChar, TRUE]; ENDLOOP; GVMailParse.FinalizeParse[pH]; EXITS parseErrorExit => { GVMailParse.FinalizeParse[pH]; RETURN[NIL]}; END; }; GetRecipients: PUBLIC PROC[sender: Viewer] RETURNS[rList: LIST OF ROPE, parseError: BOOL] = { <> text: ROPE; status: WalnutParseMsg.SendParseStatus; sPos, mPos: INT; TRUSTED {text_ WalnutDocumentRope.Create[LOOPHOLE [TiogaOps.ViewerDoc[sender]]]}; [status, sPos, mPos, rList]_ WalnutParseMsg.Parse[text]; IF (status # ok) AND (status # includesPublicDL) THEN BEGIN SELECT status FROM fieldNotAllowed => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]]; SenderReport[" field is not allowed\n"]} ELSE SenderReport[IO.PutFR[" field at pos %g is not allowed\n", IO.int[sPos]]]; syntaxError => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport["\nSyntax error on line beginning with "]; SenderReport[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]]} ELSE SenderReport[IO.PutFR["..... Syntax error at position %g ", IO.int[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] }; END.