File: PeanutSendMailImpl.mesa
April 1, 1983 3:02 pm
Edited by Doug Wyatt, April 19, 1983 2:07 pm
Last Edited by: Pausch, July 18, 1983 2:13 pm
DIRECTORY
GVAnswer USING [MakeHeader],
GVBasics USING [ItemType, RName],
GVMailParse,
GVNames,
GVSend USING [AddRecipient, AddToItem, CheckValidity, Close, Create, Handle, Send, SendFailed, StartItem, StartSend, StartSendInfo],
List USING [Append],
InputFocus USING [GetInputFocus],
Menus USING [MouseButton],
IO,
PeanutRetrieve USING [CopyMessages],
Rope,
TextNode,
TiogaOps,
ViewerEvents,
ViewerOps,
ViewerTools,
ViewerClasses,
UserCredentials USING [GetUserCredentials],
UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc, Token],
PeanutSendMail,
PeanutWindow,
PeanutParse;
PeanutSendMailImpl:
CEDAR
MONITOR
IMPORTS
GVAnswer, GVMailParse, GVNames, GVSend, List, IO, PeanutRetrieve, Rope, TiogaOps, ViewerTools, TextNode, UserCredentials, UserProfile, PeanutWindow, ViewerEvents, ViewerOps
EXPORTS PeanutSendMail, PeanutParse
SHARES Rope =
BEGIN OPEN PeanutSendMail, PeanutParse;
Global types & variables
TiogaCTRL: GVBasics.ItemType;
Viewer: TYPE = ViewerClasses.Viewer;
ROPE: TYPE = Rope.ROPE;
RName: TYPE = GVBasics.RName;
userRName: PUBLIC ROPE← NIL; -- user name with registry
simpleUserName: PUBLIC ROPE← NIL; -- user name without registry
defaultRegistry: PUBLIC ROPE← "pa";
arpaGatewayHosts: PUBLIC LIST OF ROPE← LIST["PARC-MAXC", "PARC", "MAXC"];
needToAuthenticate: BOOL← TRUE;
subTocc:
ROPE←
"Subject: \001Topic\002\nTo: \001Recipients\002\ncc: \001Copies To\002";
messageRope: ROPE← "\n\n\001Message\002\n";
answRope: ROPE← "\n\n\001Message\002\n";
fwdRope: ROPE← "\n\n\001CoveringMessage\002\n\n-------------------------------------\n";
fwdRope2: ROPE← "\n------------------------------------------------------------\n";
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]
];
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AuthenticateUser:
PUBLIC
PROC
RETURNS [
BOOL] =
BEGIN
uN: ROPE← UserCredentials.GetUserCredentials[].name;
proc: PROC[r: ROPE] = {InternalReport[r]};
pos: INT;
auth: GVNames.AuthenticateInfo;
IF Rope.Length[uN] = 0
THEN {proc["Please Login"];
RETURN[
FALSE]};
check if userRName is already set
IF userRName =
NIL
THEN
IF (pos←Rope.SkipTo[s: uN, skip: "."]) = Rope.Length[uN]
THEN {simpleUserName← uN; userRName← Rope.Cat[uN, ".", defaultRegistry]}
ELSE {simpleUserName← Rope.Substr[uN, 0, pos]; userRName← uN};
SELECT
auth← GVNames.Authenticate[userRName, UserCredentials.GetUserCredentials[].password] FROM
group => proc["... Can't login as group"];
individual => {needToAuthenticate← FALSE; RETURN[TRUE]};
notFound => {proc[uN]; proc[" is invalid - please Login"]};
allDown => proc["... No server responded"];
badPwd => proc["... Your Password is invalid - please Login"];
ENDCASE;
RETURN[FALSE];
END;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
PeanutSendInit: UserProfile.ProfileChangedProc = { needToAuthenticate←
TRUE };
ObjectRope: TYPE = object node Rope.RopeRep;
CreateMessageRope:
PUBLIC
PROC [firstMessageNode: TextNode.Ref]
RETURNS [r: Rope.
ROPE] = {
RETURN [
NEW[ObjectRope ←
[node[object[2048,firstMessageNode,FetchFromMessageRope,NIL,NIL]]]]] };
FetchFromMessageRope:
PROC [data:
REF, index:
INT]
RETURNS [
CHAR] = {
firstMessageNode: TextNode.Ref ← NARROW[data];
loc: TextNode.Location = TextNode.LocRelative[[firstMessageNode,0], index];
n: TextNode.RefTextNode = TextNode.NarrowToTextNode[loc.node];
IF n=NIL THEN RETURN[0C];
IF loc.where >= Rope.Size[n.rope] THEN RETURN ['\n];
RETURN [Rope.Fetch[n.rope, loc.where]] };
TopParent:
PROC [node, root: TiogaOps.Ref ←
NIL]
RETURNS [parent: TiogaOps.Ref] = {
IF node=NIL THEN RETURN [NIL];
IF root=NIL THEN root ← TiogaOps.Root[node];
DO
parent ← TiogaOps.Parent[node];
IF parent=root THEN RETURN [node];
node ← parent;
ENDLOOP };
AnswerMsg:
PUBLIC
PROC [mouseButton: Menus.MouseButton, shift, control:
BOOL] =
BEGIN
notOk: BOOL;
errorIndex: INT;
txt, answer: ROPE;
messageNode: TiogaOps.Ref;
AnswerGetChar: PROC[pos: INT] RETURNS[CHAR] = {RETURN[txt.Fetch[pos]]};
IF needToAuthenticate AND ~AuthenticateUser[] THEN RETURN;
messageNode ← TopParent[TiogaOps.GetSelection[].start.node];
IF messageNode =
NIL
THEN {
InternalReport["\nSelect message to be answered."]; RETURN };
TRUSTED {txt ← CreateMessageRope[LOOPHOLE[TiogaOps.FirstChild[messageNode]]] };
[notOk, answer, errorIndex]← GVAnswer.MakeHeader[AnswerGetChar, txt.Length[],
IF UserProfile.Boolean[key:"Peanut.CarbonCopyToSelf",default:TRUE] THEN simpleUserName ELSE "", defaultRegistry];
IF notOk
THEN {
InternalReport[
IO.PutFR[
"\nSyntax error in line previous to line containing pos %g (in message being answered)",
IO.int[errorIndex]]];
RETURN };
NewForm[Rope.Cat[answer, answRope]];
END;
ForwardMsg:
PUBLIC
PROC [mouseButton: Menus.MouseButton, shift, control:
BOOL] = {
sourceViewer: Viewer;
lockedPrimary, lockedSecondary, lockedDest, lockedSource: BOOL ← FALSE;
start, end: TiogaOps.Location;
sourceDoc, destDoc, messageHeader: TiogaOps.Ref;
singleNode: BOOL;
This probably should be revised to work if selection is in the message header rather than the message body.
Simple:
PROC [node: TiogaOps.Ref]
RETURNS [
BOOL] = {
n: TextNode.RefTextNode;
TRUSTED { n ← TextNode.NarrowToTextNode[LOOPHOLE[node]] };
IF n=NIL THEN RETURN [FALSE];
singleNode ← n.child=NIL AND n.last;
IF ~singleNode THEN RETURN [FALSE];
RETURN [n.runs=NIL] };
Cleanup:
PROC = {
IF lockedPrimary THEN TiogaOps.UnlockSel[primary];
IF lockedSecondary THEN TiogaOps.UnlockSel[secondary];
IF lockedDest THEN TiogaOps.Unlock[destDoc];
IF lockedSource THEN TiogaOps.Unlock[sourceDoc];
};
IF needToAuthenticate AND ~AuthenticateUser[] THEN RETURN;
TiogaOps.LockSel[primary]; lockedPrimary ← TRUE;
[sourceViewer, start, end, ----, ----, ----] ← TiogaOps.GetSelection[];
IF sourceViewer =
NIL
THEN {
TiogaOps.UnlockSel[primary]; InternalReport["\nSelect message to be forwarded."]; RETURN };
IF start.node=end.node
AND Simple[start.node]
THEN
-- send without Tioga formatting
NewForm[Rope.Cat["Subject: \001Topic\002\nTo: \001Recipients\002\nReply-To: ", simpleUserName, "\ncc: \001Copies To\002", fwdRope, TiogaOps.GetRope[start.node], fwdRope2]]
ELSE {
-- forward with Tioga formatting
OPEN TiogaOps;
destViewer: Viewer;
sourceDoc ← ViewerDoc[sourceViewer];
messageHeader ← TopParent[start.node, sourceDoc];
IF TopParent[end.node, sourceDoc] # messageHeader
THEN {
UnlockSel[primary]; InternalReport["\nSelect single message to be forwarded."]; RETURN };
destViewer ← ViewerTools.MakeNewTextViewer[info: [name: "Message", icon: PeanutWindow.mailMessageIcon, iconic: FALSE]];
[] ← ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenEdited, event: edit, filter: destViewer, before: TRUE];
[] ← ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenSaved, event: save, filter: destViewer, before: FALSE];
destDoc ← ViewerDoc[destViewer];
LockSel[secondary]; lockedSecondary ← TRUE;
Lock[destDoc]; lockedDest ← TRUE;
IF sourceDoc#destDoc THEN { Lock[sourceDoc]; lockedSource ← TRUE };
SelectPoint[destViewer, [FirstChild[destDoc],0], primary];
InsertRope["Subject: \001Topic\002\nTo: \001Recipients\002\nReply-To: "];
InsertRope[simpleUserName];
InsertRope["\ncc: \001Copies To\002"];
InsertRope[fwdRope];
IF ~singleNode THEN BackSpace[]; -- get rid of CR at end
SelectBranches[
-- source
viewer: sourceViewer, level: IF singleNode THEN char ELSE branch, caretBefore: FALSE,
pendingDelete: FALSE, which: secondary,
start: FirstChild[messageHeader], end: LastChild[messageHeader]];
ToPrimary[];
SelectPoint[destViewer, [FirstChild[destDoc],0], primary];
[] ← NextPlaceholder[gotoend: TRUE];
};
Cleanup[];
};
MailMessageHasBeenEdited:
PROC [viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before:
BOOL]
RETURNS[abort:
BOOL ←
FALSE] = {
IF before
THEN {
viewer.icon ← PeanutWindow.dirtyMailMessageIcon;
IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; };
};
MailMessageHasBeenSaved:
PROC [viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before:
BOOL]
RETURNS[abort:
BOOL ←
FALSE] = {
IF
NOT before
AND viewer.file #
NIL
THEN {
viewer.icon ← PeanutWindow.mailMessageIcon;
IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; };
};
MailMessageHasBeenReset: TiogaOps.CommandProc
[viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL ← FALSE]
= {
viewer.icon ← PeanutWindow.mailMessageIcon;
IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all];
};
NewMsgForm:
PUBLIC
PROC [mouseButton: Menus.MouseButton, shift, control:
BOOL] = {
NewForm[Rope.Cat[
"Subject: \001Topic\002\nTo: \001Recipients\002\nReply-To: ",
simpleUserName, "\ncc: \001Copies To\002", IF UserProfile.Boolean["Peanut.CarbonCopyToSelf",TRUE] THEN Rope.Concat[", " , simpleUserName] ELSE "",
messageRope]]};
NewForm:
PROC [r:
ROPE] = {
newForm: Viewer;
ViewerTools.SetContents[
newForm ← ViewerTools.MakeNewTextViewer[info: [name: "Message", icon: PeanutWindow.mailMessageIcon, iconic: FALSE]], r];
[] ← ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenEdited, event: edit, filter: newForm, before: TRUE];
[] ← ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenSaved, event: save, filter: newForm, before: FALSE];
TiogaOps.RegisterCommand[name: $reset, proc: MailMessageHasBeenReset, before: FALSE];
ViewerTools.SetSelection[newForm, NEW[ViewerTools.SelPosRec← [0, 0]]];
newForm.class.notify[newForm, LIST[$NextPlaceholder]]; };
InternalReport:
PROC [r:
ROPE] =
INLINE { PeanutWindow.OutputRope[r] };
CheckForAbort:
PROC
RETURNS [
BOOL] =
INLINE {
RETURN [PeanutWindow.abortFlag] };
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SendMsg:
PUBLIC
PROC [mouseButton: Menus.MouseButton, shift, control:
BOOL] =
BEGIN
senderV: Viewer;
fullSender, oldName: ROPE;
restore: BOOL ← FALSE;
Restore:
PROC = {
senderV.name ← oldName;
ViewerTools.EnableUserEdits[senderV];
ViewerOps.OpenIcon[senderV] };
BEGIN
ENABLE UNWIND => { IF restore THEN Restore[] };
status: SendParseStatus;
sPos, mPos: INT;
specialTxt, formatting: ROPE;
smr: SendingRec;
contents: ViewerTools.TiogaContents;
this needs to be done first, before the user can change the selection out from under me ...
the "right" way would be to access the sending viewer later and make tioga lower level
calls, but CopyMessages is easier to use, so I just call it before the selection can change.
NOTE, however that if the message has troubles being sent, this copy will still work ...
IF
NOT Rope.Equal[UserProfile.Token[key: "Peanut.OutGoingMailFile", default:""],""]
THEN
PeanutRetrieve.CopyMessages[to: UserProfile.Token[key: "Peanut.OutGoingMailFile", default:""], delete: FALSE];
IF needToAuthenticate AND ~AuthenticateUser[] THEN RETURN;
PeanutWindow.abortFlag ← FALSE;
senderV ← ViewerTools.GetSelectedViewer[];
IF senderV = NIL THEN { InternalReport["\nSelect message to be sent."]; RETURN };
oldName ← senderV.name;
senderV.name ← "Sending"; restore ← TRUE;
ViewerOps.CloseViewer[senderV];
TRUSTED {smr←
NEW[SendMsgRecObject←
[fullText: CreateMessageRope[LOOPHOLE[TiogaOps.ViewerDoc[senderV]]]]] };
InternalReport["\nParsing..."];
[status, sPos, mPos]← ParseTextToBeSent[smr];
IF (status # ok)
AND (status # includesPublicDL)
THEN
BEGIN
SELECT status
FROM
fieldNotAllowed =>
IF sPos # mPos
THEN
{ InternalReport[Rope.Substr[smr.fullText, sPos, mPos-sPos-1]];
InternalReport[" field is not allowed."]}
ELSE InternalReport[IO.PutFR[" field at pos %g is not allowed", IO.int[sPos]]];
syntaxError =>
IF sPos # mPos
THEN
{ InternalReport["\nSyntax error on line beginning with "];
InternalReport[Rope.Substr[smr.fullText, sPos, mPos-sPos-1]]}
ELSE InternalReport[IO.PutFR["..... Syntax error at position %g ", IO.int[sPos]]];
includesPrivateDL => InternalReport[" Private dl's are not yet implemented"];
ENDCASE => ERROR;
Restore;
RETURN;
END;
IF CheckForAbort[]
THEN {
Restore; InternalReport["... Message NOT sent."];
RETURN };
IF smr.arpaRecipient THEN fullSender← Rope.Concat[simpleUserName, "@Parc"]
ELSE fullSender← userRName;
specialTxt← Rope.Cat["Date: ",
IO.PutFR[
NIL,
IO.time[]],
IF smr.from = NIL THEN "\nFrom: " ELSE "\nSender: ", fullSender, "\n"];
InsertIntoSender[senderV, specialTxt, 0];
contents← ViewerTools.GetTiogaContents[senderV];
IF (formatting← contents.formatting).Length[] = 0 THEN smr.fullText← contents.contents
ELSE {
-- check for null at end of contents; move it to formatting
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] };
InternalReport["... Sending message..."];
IF Send[smr, formatting]
THEN {
InternalReport[" ... Message has been delivered"];
ViewerOps.DestroyViewer[senderV] }
ELSE {
DeleteFromSender[senderV, specialTxt, 0];
Restore;
InternalReport["... Message NOT sent."] };
END; END;
DeleteFromSender:
PROC[v: Viewer, what:
ROPE, where:
INT] =
BEGIN OPEN TiogaOps;
thisV: Ref← ViewerDoc[v];
DeleteChars:
PROC[root: Ref] =
BEGIN
startLoc: Location;
prevV: Viewer;
prevStart, prevEnd: Location;
prevLevel: SelectionGrain;
cb, pd: BOOL;
startLoc← LocRelative[[FirstChild[thisV], 0], where];
[prevV, prevStart, prevEnd, prevLevel, cb, pd]← GetSelection[primary];
ViewerTools.EnableUserEdits[v];
SelectPoint[v, startLoc, primary];
DeleteNextCharacter[Rope.Size[what]];
ViewerTools.InhibitUserEdits[v];
IF (prevV # v)
AND (prevV#
NIL)
THEN
SetSelection[prevV, prevStart, prevEnd, prevLevel, cb, pd];
END;
CallWithLocks[DeleteChars, thisV];
END;
InsertIntoSender:
PROC[v: Viewer, what:
ROPE, where:
INT] =
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];
InsertRope[what];
ViewerTools.InhibitUserEdits[v];
IF (prevV # v)
AND (prevV#
NIL)
THEN
SetSelection[prevV, prevStart, prevEnd, prevLevel, cb, pd];
END;
CallWithLocks[InsertChars, thisV];
END;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Send:
PROC[smr: SendingRec, formatting:
ROPE]
RETURNS[ sent:
BOOLEAN] = {
sendHandle: GVSend.Handle ← GVSend.Create[];
sent ← SendMessage[smr, formatting, sendHandle,
TRUE ! GVSend.SendFailed =>
IF notDelivered
THEN {
InternalReport["\nCommunication failure during send."];
sent ← FALSE; CONTINUE }
ELSE {
InternalReport["\nCommunication failure, but message delivered."];
sent ← TRUE; CONTINUE; };
];
IF sendHandle#NIL THEN GVSend.Close[sendHandle];
SendMessage:
PROC[smr: SendingRec, formatting:
ROPE, h: GVSend.Handle, validateFlag:
BOOL]
RETURNS[ sent: BOOLEAN ] = {
DO ENABLE GVSend.SendFailed => CONTINUE; -- try again if SendFailed
startInfo: GVSend.StartSendInfo;
stepper: LIST OF RName;
numRecips: INT ← 0;
numValidRecips: INT;
firstInvalidUser: BOOL← TRUE;
numInvalidUsers: INTEGER← 0;
ReportFromSend: PROC[r: ROPE] = { InternalReport[r]};
InvalidUserProc:
PROC [ userNum:
INT, userName: RName ] = {
IF firstInvalidUser THEN {ReportFromSend["\nInvalid user(s): "]; firstInvalidUser← FALSE};
SELECT numInvalidUsers ← numInvalidUsers + 1
FROM
1 => ReportFromSend[userName];
IN [2..5] => {ReportFromSend[", "]; ReportFromSend[userName]};
6 => ReportFromSend[", ..."];
ENDCASE;
} ;
sent ← FALSE ;
startInfo ← GVSend.StartSend[ handle: h,
senderPwd: UserCredentials.GetUserCredentials[].password,
sender: userRName,
returnTo: userRName,
validate: validateFlag
] ;
SELECT startInfo
FROM
badPwd => {ReportFromSend["\nInvalid password"]; RETURN};
badSender => {ReportFromSend["\nInvalid sender name"]; RETURN};
badReturnTo => {ReportFromSend["\nBad return-to field"]; RETURN};
allDown => {ReportFromSend["\nAll servers are down"]; RETURN};
ok => {
stepper ← smr.to;
WHILE stepper #
NIL
DO
GVSend.AddRecipient[ h, stepper.first ];
numRecips ← numRecips + 1;
stepper ← stepper.rest;
ENDLOOP;
IF CheckForAbort[] THEN RETURN;
stepper ← smr.cc;
WHILE stepper #
NIL
DO
GVSend.AddRecipient[ h, stepper.first ] ;
numRecips ← numRecips + 1 ;
stepper ← stepper.rest ;
ENDLOOP ;
IF CheckForAbort[] THEN RETURN;
IF validateFlag
THEN {
IF (numValidRecips← GVSend.CheckValidity[ h, InvalidUserProc]) = 0
THEN {
ReportFromSend["\nThere were NO valid recipients."]; RETURN };
IF numValidRecips # numRecips
THEN {
tempInteger: INT ← numRecips-numValidRecips;
ReportFromSend[IO.PutFR["\nThere were %g invalid recipients,", IO.int[tempInteger] ]];
RETURN;
};
};
IF CheckForAbort[] THEN RETURN;
ReportFromSend[IO.PutFR["..sending to %g recipients", IO.int[numValidRecips]]];
validateFlag← FALSE; -- if sending fails, don't need to re-validate
GVSend.StartItem[h, Text];
GVSend.AddToItem[h, smr.fullText];
IF formatting#
NIL
THEN {
-- send the formatting info as a second item
GVSend.StartItem[h, TiogaCTRL]; GVSend.AddToItem[h, formatting] };
IF CheckForAbort[] THEN RETURN;
GVSend.Send[ h ] ;
sent← TRUE;
RETURN;
} ;
ENDCASE ;
ENDLOOP;
} ;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
CanonicalName:
PUBLIC
PROC [simpleName, registry:
ROPE]
RETURNS[name:
ROPE] =
BEGIN
name← simpleName;
IF registry.Length[] = 0
THEN name← name.Cat[".", defaultRegistry]
ELSE name← name.Cat[".", registry];
END;
ParseTextToBeSent:
PROC[msg: SendingRec]
RETURNS[status: SendParseStatus, sPos, mPos:
INT] =
BEGIN OPEN GVMailParse;
mLF: 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;
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: MessageFieldIndex] =
BEGIN
fieldBody, fbEnd: LIST OF RName← NIL;
AnotherRName:
PROC[r1, r2:
ROPE, isFile, isNested:
BOOL]
RETURNS [
ROPE,
BOOLEAN] =
BEGIN
name: ROPE;
name← 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
ELSE
{
IF status # includesPrivateDL
THEN status← includesPublicDL;
dlCount← dlCount + 1
};
RETURN[NIL, FALSE];
ParseNameList[pH, GetNextMsgChar, AnotherRName, NIL];
SELECT index
FROM
toF =>
IF msg.to =
NIL
THEN msg.to← fieldBody
ELSE
IF fieldBody#
NIL
THEN
TRUSTED
{msg.to← LOOPHOLE[List.Append[LOOPHOLE[msg.to], LOOPHOLE[fieldBody]]]};
ccF, cF, bccF =>
IF msg.cc =
NIL
THEN msg.cc← fieldBody
ELSE
IF fieldBody#
NIL
THEN
TRUSTED
{msg.cc← LOOPHOLE[List.Append[LOOPHOLE[msg.cc], LOOPHOLE[fieldBody]]]};
ENDCASE => ERROR;
END;
pH: ParseHandle;
field: ROPE← NIL;
fieldNotRecognized: BOOL;
mPos← 0; -- where we are in the fulltext, for parsing
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: MessageFieldIndex IN MessageFieldIndex DO
{ mLF← messageParseArray[i];
IF Rope.Equal[messageParseArray[i].name, field,
FALSE]
THEN
-- ignore case
{ fieldNotRecognized←
FALSE;
SELECT mLF.fType
FROM
simpleRope =>
IF i = fromF
THEN msg.from← GetFieldBody[pH, GetNextMsgChar]
ELSE
{
IF i = replyToF
THEN msg.replyTo←
TRUE;
[]← 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;
now we are positioned at the beginning of the body of the message
FinalizeParse[pH];
msg.endHeadersPos← mPos - 1;
if any recipient is at another arpa site, all recipients should be arpa qualified
however, like Laurel, we'll only do the From/Sender field in full arpa regalia
msg.numRecipients← countOfRecipients;
msg.numDLs← dlCount;
EXITS
errorExit => RETURN[syntaxError, sPos, mPos];
END;
TRUSTED { TiogaCTRL ← LOOPHOLE[1013B] };
UserProfile.CallWhenProfileChanges[PeanutSendInit];
END.