<> <> <> <> <> <> <<>> <> DIRECTORY BasicTime USING [GMT, nullGMT, Unpacked, Now, Unpack], FS USING [ComponentPositions, Error, ExpandName, StreamOpen], GVBasics USING [ItemType, RName], GVMailParse, GVNames, GVSend USING [Abort, AddRecipient, AddToItem, CheckValidity, Create, Handle, Send, SendFailed, StartItem, StartSend, StartSendInfo], Icons, Menus, IO, Rope, RopeList USING [Append], RuntimeError USING [BoundsFault], TextNode, TiogaOps, UserProfile USING [Token], ViewerClasses USING [Viewer], ViewerOps, ViewerTools, UserCredentials USING [Get], WalnutDocumentRope, WalnutSendOps, WalnutSendOpsExtras, WalnutSendInternal, WalnutParseMsg USING [MessageFieldIndex, MessageInfo, messageParseArray, CanonicalName]; WalnutSendMailImpl: CEDAR MONITOR IMPORTS BasicTime, FS, GVMailParse, GVNames, GVSend, IO, Rope, RopeList, RuntimeError, TextNode, TiogaOps, ViewerOps, ViewerTools, UserCredentials, UserProfile, WalnutParseMsg, WalnutSendInternal, WalnutSendOps, WalnutSendOpsExtras EXPORTS WalnutDocumentRope, WalnutSendInternal, WalnutSendOps = BEGIN OPEN WalnutSendInternal, WalnutSendOpsExtras; RName: TYPE = GVBasics.RName; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; <<************************************************************************>> Send: PUBLIC PROC[v: Viewer, doClose: BOOL_ FALSE] RETURNS[sent: BOOL] = { oldMenu: Menus.Menu = v.menu; v.inhibitDestroy_ TRUE; BEGIN ENABLE UNWIND => GOTO out; ViewerOps.SetMenu[v, sendingMenu]; IF needToAuthenticate THEN { SenderReport["Authenticating user ..."]; IF ~AuthenticateUser[] THEN {ViewerOps.BlinkIcon[v, IF v.iconic THEN 0 ELSE 1]; ViewerOps.SetMenu[v, oldMenu]; v.inhibitDestroy _ FALSE; RETURN[FALSE] }; SenderReport[" ...ok\n"]; }; sent _ InternalSendMsg[v, doClose]; EXITS out => NULL; END; ViewerOps.SetMenu[v, oldMenu]; v.inhibitDestroy _ FALSE; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> AppendHeaderLine: PUBLIC PROC[v: Viewer, line: ROPE, changeSelection: BOOL _ FALSE] = BEGIN ENABLE RuntimeError.BoundsFault => GOTO exit; text: ROPE; i: INT _ 0; ch: CHAR; TRUSTED {text _ Create[LOOPHOLE [TiogaOps.ViewerDoc[v]]]}; DO -- find the double CR at the end of the headers UNTIL (ch _ text.Fetch[i]) = '\n DO i _ i + 1; ENDLOOP; IF (ch _ text.Fetch[i _ i + 1]) = '\n THEN EXIT; ENDLOOP; InsertIntoViewer[v, line, i-1, WalnutSendOps.labelFont, changeSelection]; ViewerTools.EnableUserEdits[v]; EXITS exit => SenderReport[IO.PutFR["Malformed headers; append of &g not done", IO.rope[line]]]; END; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> AuthenticateUser: PROC RETURNS [BOOL] = BEGIN OPEN WalnutSendOps; auth: GVNames.AuthenticateInfo; IF userRName.Length[] = 0 THEN {SenderReport["Please Login\n"]; RETURN[FALSE]}; SELECT auth _ GVNames.Authenticate[userRName, UserCredentials.Get[].password] FROM group => SenderReport["... Can't login as group\n"]; individual => {needToAuthenticate _ FALSE; RETURN[TRUE]}; notFound => {SenderReport[userRName]; SenderReport[" is invalid - please Login\n"]}; allDown => SenderReport["... No server responded\n"]; badPwd => SenderReport["... Your Password is invalid - please Login\n"]; ENDCASE; RETURN[FALSE]; END; InternalSendMsg: PROC[senderV: Viewer, doClose: BOOL] RETURNS[sendOk: BOOL] = BEGIN status: SendParseStatus; sPos, mPos: INT; formatting: ROPE; smr: SendingRec; addedTxtLength: INT; newTxt: ROPE; contents, currentText: ViewerTools.TiogaContents; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]]; Blink: PROC[v: Viewer] = { ViewerOps.BlinkIcon[v, IF v.iconic THEN 0 ELSE 1]}; sendOk _ FALSE; senderInfo.aborted _ FALSE; IF senderInfo.successfullySent AND ~senderV.newVersion THEN { SenderReport["\nDo you really want to send this message again?"]; IF ~Confirmation[senderInfo] THEN { SenderReport[" .. Not sent\n"]; senderInfo.successfullySent _ FALSE; RETURN} }; senderInfo.successfullySent _ FALSE; SenderReport["... Parsing..."]; smr _ NEW[SendMsgRecObject]; TRUSTED { smr.fullText _ Create[LOOPHOLE [TiogaOps.ViewerDoc[senderV]]] }; [status, sPos, mPos] _ ParseTextToBeSent[smr]; SELECT status FROM fieldNotAllowed => { IF sPos # mPos THEN { ShowErrorFeedback[senderV, sPos, mPos]; SenderReport[Rope.Substr[smr.fullText, 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]]]; Blink[senderV]; RETURN }; syntaxError => { IF sPos # mPos THEN { ShowErrorFeedback[senderV, sPos, mPos]; SenderReport["\nSyntax error on line beginning with "]; SenderReport[Rope.Substr[smr.fullText, MAX[0, sPos-1], mPos-sPos]]} ELSE SenderReport[IO.PutFR["..... Syntax error at position %g ", IO.int[sPos]]]; Blink[senderV]; RETURN }; pdlNotFound, pdlSyntaxError => { Blink[senderV]; RETURN }; ENDCASE => NULL; IF CheckForAbortSend[senderInfo] THEN RETURN; IF (status = includesPublicDL OR status = includesPrivateDL OR smr.numRecipients > maxWithNoReplyTo) AND ~smr.replyTo THEN { howToReply: HowToReplyTo _ self; IF ~replyToSelf THEN { oldM: Menus.Menu _ senderV.menu; IF CheckForAbortSend[senderInfo] THEN RETURN; ViewerOps.SetMenu[senderV, replyToMenu]; SenderReport[ IO.PutFR["... %g public DLs and %g other recipients; please choose Reply-To option", IO.int[smr.numDLs], IO.int[smr.numRecipients]]]; SenderReport[ "\nClick Self to reply-to self, All to reply-to all, Cancel to cancel Send\n"]; howToReply _ ReplyToResponse[senderInfo]; ViewerOps.SetMenu[senderV, oldM]; }; IF howToReply # all THEN InternalInsert[senderV, "Reply-to: ", WalnutSendOps.userRName, smr.endHeadersPos-1, WalnutSendOps.labelFont]; IF howToReply = cancel THEN {SenderReport["\nDelivery cancelled. Reply-to: has been added\n"]; RETURN }; IF CheckForAbortSend[senderInfo] THEN RETURN; }; IF doClose AND ~senderV.iconic THEN ViewerOps.CloseViewer[senderV]; <> currentText _ ViewerTools.GetTiogaContents[senderV]; newTxt _ Rope.Cat[IF smr.from = NIL THEN "From: " ELSE "Sender: ", WalnutSendOps.userRName]; addedTxtLength _ newTxt.Length[]; InsertIntoViewer[senderV, newTxt, 0, WalnutSendOps.labelFont]; <> newTxt _ Rope.Concat["Date: ", InternalRFC822Date[BasicTime.nullGMT, TRUE]]; addedTxtLength _ newTxt.Length[] + addedTxtLength + 1; InsertIntoViewer[senderV, newTxt, 0, WalnutSendOps.labelFont]; contents _ ViewerTools.GetTiogaContents[senderV]; <> { last: INT _ contents.contents.Length[] - 1; IF contents.contents.Fetch[last] = '\000 THEN -- NULL for padding { smr.fullText _ Rope.Substr[contents.contents, 1, last-1]; formatting _ Rope.Concat["\000", contents.formatting] } ELSE { smr.fullText _ Rope.Substr[contents.contents, 1]; formatting _ contents.formatting } }; IF smr.subject.Length[] > 40 THEN smr.subject _ Rope.Concat[Rope.Substr[smr.subject, 0, 35], " ..."]; smr.subject _ Rope.Cat[" \"", smr.subject, "\" "]; SenderReport["... Sending "]; SenderReport[smr.subject]; IF senderInfo.successfullySent _ sendOk _ SendIt[smr, senderInfo, formatting] THEN { SenderReport[smr.subject]; SenderReport[" has been delivered\n"]; senderInfo.prevMsg _ currentText; } ELSE { DeleteChars[senderV, addedTxtLength]; SenderReport[smr.subject]; SenderReport[" NOT sent\n"]; }; END; RFC822Date: PUBLIC PROC[gmt: BasicTime.GMT _ BasicTime.nullGMT] RETURNS[date: ROPE] = <> { RETURN[InternalRFC822Date[gmt, FALSE]] }; InternalRFC822Date: PROC[gmt: BasicTime.GMT, withDay: BOOL] RETURNS[date: ROPE] = <> BEGIN OPEN IO; upt: BasicTime.Unpacked _ BasicTime.Unpack[IF gmt = BasicTime.nullGMT THEN BasicTime.Now[] ELSE gmt]; zone: ROPE; 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; 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]]; IF withDay THEN { dayOfWeek: ROPE; SELECT upt.weekday FROM Monday => dayOfWeek _ "Mon, "; Tuesday => dayOfWeek _ "Tue, "; Wednesday => dayOfWeek _ "Wed, "; Thursday => dayOfWeek _ "Thu, "; Friday => dayOfWeek _ "Fri, "; Saturday => dayOfWeek _ "Sat, "; Sunday => dayOfWeek _ "Sun, "; ENDCASE => dayOfWeek _ ""; date _ dayOfWeek.Concat[date]; }; END; ShowErrorFeedback: PUBLIC PROC[v: Viewer, start, end: INT] = BEGIN OPEN TiogaOps; ENABLE UNWIND => GOTO exit; startLoc, endLoc: Location; thisV: Ref = ViewerDoc[v]; beginning: Location _ [FirstChild[thisV], 0]; startLoc _ LocRelative[beginning, start]; endLoc _ LocRelative[beginning, end]; SetSelection[viewer: v, start: startLoc, end: endLoc, which: feedback]; EXITS exit => NULL; END; InsertIntoViewer: PUBLIC PROC [v: Viewer, what: ROPE, where: INT, labelFont: ROPE, changeSelection: BOOL _ FALSE] = { prefix: ROPE _ Rope.Substr[ what, 0, Rope.Find[s1: what, s2: ":"] ]; field: ROPE _ Rope.Replace[base: what, start: 0, len: prefix.Length, with: NIL]; InternalInsert[v, prefix, field, where, labelFont, changeSelection]; }; InternalInsert: PROC [v: Viewer, prefix, field: ROPE, where: INT, labelFont: ROPE, changeSelection: BOOL _ FALSE] = BEGIN OPEN TiogaOps; thisV: Ref = ViewerDoc[v]; InsertChars: PROC[root: Ref] = BEGIN insertLoc: Location; prevV: Viewer; prevStart, prevEnd: Location; prevLevel: SelectionGrain; cb, pd: BOOL; IF where < 0 THEN insertLoc _ LastLocWithin[LastChild[thisV]] ELSE insertLoc _ LocRelative[[FirstChild[thisV], 0], where]; [prevV, prevStart, prevEnd, prevLevel, cb, pd] _ GetSelection[primary]; ViewerTools.EnableUserEdits[v]; SelectPoint[v, insertLoc, primary]; IF where # 0 THEN TiogaOps.Break[]; -- make a new node IF labelFont # NIL THEN TiogaOps.SetLooks[labelFont, caret]; TiogaOps.InsertRope[prefix]; IF labelFont # NIL THEN TiogaOps.ClearLooks[caret]; TiogaOps.InsertRope[field]; IF where = 0 THEN TiogaOps.Break[]; -- make a new node ViewerTools.InhibitUserEdits[v]; IF ~changeSelection AND (prevV # v) AND (prevV#NIL) AND ~prevV.destroyed THEN SetSelection[prevV, prevStart, prevEnd, prevLevel, cb, pd ! TiogaOps.SelectionError => CONTINUE]; END; LockViewerOpen[v]; IF thisV = NIL THEN InsertChars[thisV] ELSE CallWithLocks[InsertChars, thisV]; ReleaseOpenViewer[v]; END; DeleteChars: PUBLIC ENTRY PROC[v: Viewer, num: INT] = BEGIN ENABLE UNWIND => NULL; IF num # 0 THEN DeleteLeadingChars[v, num] END; DeleteLeadingChars: INTERNAL PROC[v: Viewer, num: INT] = BEGIN OPEN TiogaOps; thisV: Ref _ ViewerDoc[v]; DelChars: PROC[root: Ref] = BEGIN prevV: Viewer; prevStart, prevEnd: Location; prevLevel: SelectionGrain; cb, pd: BOOL; startLoc: Location _ [FirstChild[thisV], 0]; endLoc: Location _ LocRelative[startLoc, num]; [prevV, prevStart, prevEnd, prevLevel, cb, pd] _ GetSelection[primary]; ViewerTools.EnableUserEdits[v]; SetSelection[viewer: v, start: startLoc, end: endLoc]; Delete[]; GoToNextNode[]; Join[]; IF (prevV # v) AND (prevV#NIL) THEN SetSelection[prevV, prevStart, prevEnd, prevLevel, cb, pd]; END; IF thisV = NIL THEN DelChars[thisV] ELSE CallWithLocks[DelChars, thisV]; END; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> SendIt: PROC[smr: SendingRec, senderInfo: SenderInfo, formatting: ROPE] RETURNS[ sent: BOOLEAN] = { InitSending: ENTRY PROC = { ENABLE UNWIND => NULL; senderInfo.sendHandle _ GVSend.Create[]; senderInfo.aborted _ FALSE; }; FinishedSending: ENTRY PROC = { ENABLE UNWIND => NULL; senderInfo.sendHandle _ NIL }; InitSending[]; sent _ SendMessage[smr, senderInfo, formatting ! GVSend.SendFailed => { IF notDelivered THEN { SenderReport["\nCommunication failure during send. Retry?\n"]; IF Confirmation[senderInfo] THEN RETRY ELSE { sent _ FALSE; CONTINUE } } ELSE { SenderReport["\nCommunication failure, but message delivered\n"]; sent _ TRUE; CONTINUE; }; }]; FinishedSending[]; } ; SendMessage: PROC[smr: SendingRec, senderInfo: SenderInfo, formatting: ROPE] RETURNS[ sent: BOOLEAN ] = { DO ENABLE GVSend.SendFailed => CONTINUE; -- try again if SendFailed h: GVSend.Handle _ senderInfo.sendHandle; startInfo: GVSend.StartSendInfo; stepper: LIST OF RName; tempInteger: INT; numRecips: INT _ 0; numValidRecips: INT; firstInvalidUser: BOOL _ TRUE; numInvalidUsers: INTEGER _ 0; InvalidUserProc: PROC [ userNum: INT, userName: RName ] = { IF firstInvalidUser THEN {SenderReport["\nInvalid user(s): "]; firstInvalidUser _ FALSE}; SELECT numInvalidUsers _ numInvalidUsers + 1 FROM 1 => SenderReport[userName]; IN [2..5] => {SenderReport[", "]; SenderReport[userName]}; 6 => SenderReport[", ...\n"]; ENDCASE; } ; sent _ FALSE ; startInfo _ GVSend.StartSend[ handle: senderInfo.sendHandle, senderPwd: UserCredentials.Get[].password, sender: WalnutSendOps.userRName, returnTo: WalnutSendOps.userRName, validate: TRUE ] ; SELECT startInfo FROM badPwd => {SenderReport["\nInvalid password\n"]; RETURN}; badSender => {SenderReport["\nInvalid sender name\n"]; RETURN}; badReturnTo => {SenderReport["\nBad return-to field\n"]; RETURN}; allDown => {SenderReport["\nAll servers are down\n"]; RETURN}; ok => { stepper _ smr.to; WHILE stepper # NIL DO GVSend.AddRecipient[ h, stepper.first ]; numRecips _ numRecips + 1; stepper _ stepper.rest; ENDLOOP; IF CheckForAbortSend[senderInfo] THEN RETURN; stepper _ smr.cc; WHILE stepper # NIL DO GVSend.AddRecipient[ h, stepper.first ] ; numRecips _ numRecips + 1 ; stepper _ stepper.rest ; ENDLOOP ; IF CheckForAbortSend[senderInfo] THEN RETURN; IF (numValidRecips _ GVSend.CheckValidity[ h, InvalidUserProc]) = 0 THEN { SenderReport["\nThere were NO valid recipients; do you wish to send anyway?\n"]; IF CheckForAbortSend[senderInfo] OR ~Confirmation[senderInfo] THEN {GVSend.Abort[h]; RETURN}; }; IF numValidRecips # numRecips THEN { tempInteger _ numRecips-numValidRecips; SenderReport[IO.PutFR["\nThere were %g invalid recipients,", IO.int[tempInteger] ]]; SenderReport[" do you wish to send anyway?\n"]; IF CheckForAbortSend[senderInfo] OR ~Confirmation[senderInfo] THEN {GVSend.Abort[h]; RETURN}; }; IF CheckForAbortSend[senderInfo] THEN RETURN; SenderReport[IO.PutFR["..sending to %g recipients\n", IO.int[numValidRecips]]]; GVSend.StartItem[h, Text]; AddToItem[h, smr.fullText]; IF formatting#NIL THEN { -- send the formatting info as a second item GVSend.StartItem[h, WalnutSendOps.TiogaCTRL]; AddToItem[h, formatting] }; IF smr.voiceID.Length[] # 0 THEN { GVSend.StartItem[h, Audio]; AddToItem[h, smr.voiceID] }; ViewerOps.SetMenu[senderInfo.senderV, blankMenu]; IF CheckForAbortSend[senderInfo] THEN RETURN; GVSend.Send[ h ] ; sent _ TRUE; RETURN; } ; ENDCASE; ENDLOOP; }; AddToItem: PROC [handle: GVSend.Handle, buffer: ROPE] = { maxChunkSize: INT ~ 10000; size: INT ~ Rope.Size[buffer]; i: INT _ 0; UNTIL i = size DO chunkSize: INT ~ MIN[size-i, maxChunkSize]; GVSend.AddToItem[handle, Rope.Substr[buffer, i, chunkSize]]; i _ i + chunkSize; ENDLOOP; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> ParseTextToBeSent: PROC[msg: SendingRec] RETURNS[status: SendParseStatus, sPos, mPos: INT] = BEGIN OPEN GVMailParse; mLF: WalnutParseMsg.MessageInfo; tHeaders: LIST OF ROPE _ NIL; msgText: ROPE _ msg.fullText; lastCharPos: INT _ msgText.Length[] - 1; lastCharIsCR: BOOL _ (msgText.Fetch[lastCharPos] = '\n); countOfRecipients, dlCount: INT _ 0; pdlStatus: SendParseStatus _ ok; GetNextMsgChar: PROC[] RETURNS [ch: CHAR] = { IF mPos <= lastCharPos THEN ch _ Rope.Fetch[msgText, mPos] ELSE IF (mPos=lastCharPos+1) AND ~lastCharIsCR THEN ch _ '\n ELSE ch _ endOfInput; mPos _ mPos + 1; }; RNameListField: PROC[index: WalnutParseMsg.MessageFieldIndex] = BEGIN fieldBody, fbEnd: LIST OF RName _ NIL; ParsePvtDL: PROC [fname: ROPE] = { pdlH: ParseHandle _ NIL; -- need new handle for each PDL pdlStream: IO.STREAM _ NIL; fullName: ROPE; BEGIN ENABLE UNWIND => { IF pdlH # NIL THEN FinalizeParse[pdlH]; IF pdlStream # NIL THEN pdlStream.Close[]; }; GetPDLChar: PROC RETURNS [c: CHAR] = { -- No CR's allowed in file. IF pdlStream # NIL AND NOT pdlStream.EndOf[] THEN { IF (c _ pdlStream.GetChar[]) = '; THEN c _ endOfInput } ELSE c _ endOfInput; }; -- Extract filename from between quotes IF fname.Fetch[0] = '" THEN fname _ Rope.Substr[fname, 1, Rope.Length[fname]-2]; BEGIN cp: FS.ComponentPositions; wDir: ROPE _ UserProfile.Token["WalnutSend.DefaultDLDir"]; [fullName, cp, ] _ FS.ExpandName[fname, wDir ! FS.Error => { SenderReport[IO.PutFR["\n%g\n", IO.rope[error.explanation]]]; pdlStatus _ pdlSyntaxError; CONTINUE} ]; IF pdlStatus = pdlSyntaxError THEN RETURN; IF cp.ext.length = 0 THEN fullName _ fullName.Concat[".dl"]; END; pdlH _ InitializeParse[]; pdlStream _ FS.StreamOpen[fullName ! FS.Error => { SenderReport[IO.PutFR["\n%g\n", IO.rope[error.explanation]]]; pdlStatus _ pdlNotFound; CONTINUE} ]; IF pdlStream = NIL THEN RETURN; <<>> <> DO IF pdlStream.EndOf[] THEN { pdlStream.SetIndex[0]; EXIT } -- no : found ELSE { c: CHAR _ pdlStream.GetChar[]; IF c = ': THEN EXIT; -- leave positioned after : IF c = '\n THEN { SenderReport["\nCR's not allowed in private dl's\n"]; pdlStream.Close[]; pdlStatus _ pdlSyntaxError; RETURN; }; }; ENDLOOP; pdlH _ InitializeParse[]; ParseNameList[pdlH, GetPDLChar, AnotherRName, NIL]; FinalizeParse[pdlH]; pdlH _ NIL; pdlStream.Close[]; END; }; AnotherRName: PROC[r1, r2: ROPE, isFile, isNested: BOOL] RETURNS [ROPE, BOOLEAN] = BEGIN name: ROPE; IF isFile THEN { status _ includesPrivateDL; ParsePvtDL[r1]; RETURN[NIL, FALSE]; }; name _ WalnutParseMsg.CanonicalName[r1, r2]; IF fbEnd=NIL THEN fbEnd _ fieldBody _ CONS[name, NIL] ELSE fbEnd _ fbEnd.rest _ CONS[name, NIL]; -- IF isFile THEN status _ includesPrivateDL -- ELSE IF name.Find["^"] < 0 THEN countOfRecipients _ countOfRecipients + 1 IF name.Find["^"] < 0 AND name.Find["­"] < 0 THEN countOfRecipients _ countOfRecipients + 1 ELSE { IF status # includesPrivateDL THEN status _ includesPublicDL; dlCount _ dlCount + 1 }; RETURN[NIL, FALSE]; END; ParseNameList[pH, GetNextMsgChar, AnotherRName, NIL]; SELECT index FROM toF => IF msg.to = NIL THEN msg.to _ fieldBody ELSE IF fieldBody#NIL THEN msg.to _ RopeList.Append[msg.to, fieldBody]; ccF, cF, bccF => IF msg.cc = NIL THEN msg.cc _ fieldBody ELSE IF fieldBody#NIL THEN msg.cc _ RopeList.Append[msg.cc, fieldBody]; fromF => msg.from _ fieldBody.first; -- needs to be non-NIL replyToF => NULL; -- just to syntax check the ReplyTo field ENDCASE => ERROR; END; pH: ParseHandle; field: ROPE _ NIL; fieldNotRecognized: BOOL; mPos _ 0; -- where we are in the fulltext status _ ok; -- start with good status pH _ InitializeParse[]; DO sPos _ mPos; [field, fieldNotRecognized] _ GetFieldName[pH, GetNextMsgChar ! ParseError => { FinalizeParse[pH]; GOTO errorExit}]; IF ~fieldNotRecognized THEN EXIT; IF Rope.Equal[field, "Sender", FALSE] OR Rope.Equal[field, "Date", FALSE] THEN RETURN[fieldNotAllowed, sPos, mPos]; FOR i: WalnutParseMsg.MessageFieldIndex IN WalnutParseMsg.MessageFieldIndex DO { mLF _ WalnutParseMsg.messageParseArray[i]; IF Rope.Equal[WalnutParseMsg.messageParseArray[i].name, field, FALSE] THEN { fieldNotRecognized _ FALSE; SELECT mLF.fType FROM simpleRope => SELECT i FROM fromF => RNameListField[i ! ParseError => GOTO errorExit]; replyToF => { msg.replyTo _ TRUE; RNameListField[i ! ParseError => GOTO errorExit]; }; subjectF => msg.subject _ GetFieldBody[pH, GetNextMsgChar]; voiceF => msg.voiceID _ GetFieldBody[pH, GetNextMsgChar]; ENDCASE => [] _ GetFieldBody[pH, GetNextMsgChar, TRUE]; rCatList => [] _ GetFieldBody[pH, GetNextMsgChar, TRUE]; rNameList => RNameListField[i ! ParseError => GOTO errorExit]; ENDCASE => ERROR; EXIT }; }; ENDLOOP; IF fieldNotRecognized THEN [] _ GetFieldBody[pH, GetNextMsgChar]; -- skip anything not recognized ENDLOOP; <> FinalizeParse[pH]; msg.endHeadersPos _ mPos - 1; msg.numRecipients _ countOfRecipients; msg.numDLs _ dlCount; IF pdlStatus # ok THEN RETURN[pdlStatus, 0, 0]; EXITS errorExit => RETURN[syntaxError, sPos, mPos]; END; <> Create: PUBLIC PROC [doc: TextNode.Ref] RETURNS [r: ROPE] = { docSize: INT _ TextNode.LocOffset[[doc,0], TextNode.LastLocWithin[doc]]; r _ Rope.MakeRope[base: doc, size: docSize, fetch: Fetch]; IF docSize > 0 THEN { loc0: TextNode.Location = TextNode.LocRelative[[doc,0], 0]; loc1: TextNode.Location = TextNode.LocRelative[[doc,0], 1]; IF loc0 = loc1 THEN { <> r _ Rope.Substr[r, 1]; }; }; }; Fetch: PROC [data: REF, index: INT] RETURNS [CHAR] = { doc: TextNode.Ref _ NARROW[data]; loc: TextNode.Location = TextNode.LocRelative[[doc,0], index]; n: TextNode.RefTextNode = TextNode.NarrowToTextNode[loc.node]; IF n=NIL THEN ERROR; IF loc.where >= Rope.Size[n.rope] THEN RETURN ['\n]; RETURN [Rope.Fetch[n.rope, loc.where]] }; END.