DIRECTORY
ArpaConfig USING [bitnetGateway, uucpGateway, csnetGateway, ourLocalName, ourLocalAddress, specialDomains, validDomains, resolv],
ArpaName USING [AliasToName],
ArpaSMTPControl USING [defaultRegistry],
ArpaSMTPSupport USING [CreateSubrangeStream, Log],
ArpaSMTPSyntax USING [GVItemProc],
Basics USING [bytesPerWord],
ConvertExtras USING [RopeFromArpaAddress],
GVBasics USING [ItemHeader, RopeFromTimestamp, Timestamp],
GVProtocol USING [Failed, ReceiveCount, ReceiveItemHeader, ReceiveRName, ReceiveTimestamp],
IO USING [EndOfStream, GetBlock, GetIndex, PutBlock, PutChar, PutF, PutRope, SetIndex, STREAM],
RefText USING [AppendChar, ObtainScratch, ReleaseScratch],
Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, FromRefText, IsEmpty, Length, ROPE, Substr, Translate];
ArpaSMTPSyntaxImpl: CEDAR PROGRAM
IMPORTS ArpaConfig, ArpaName, ArpaSMTPControl, ArpaSMTPSupport, ConvertExtras, GVBasics, GVProtocol, IO, RefText, Rope
EXPORTS ArpaSMTPSyntax =
BEGIN
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;

possibleOldNames: LIST OF ROPE _ LIST["Xerox", "PARC.Xerox", "[10.2.0.32]", "Parc-Maxc"];
arpaSpecialRegistries: LIST OF ROPE _ LIST["AG", "ArpaGateway", "NotArpa"];

EnumerateGVItems: PUBLIC PROC [GVStream: STREAM, proc: ArpaSMTPSyntax.GVItemProc,
 procData: REF ANY _ NIL] = {
nextItemIndex: INT;
continue: BOOL;
itemHeader: GVBasics.ItemHeader;
DO
itemHeader _ GVProtocol.ReceiveItemHeader[GVStream];
nextItemIndex _ GVStream.GetIndex[] +
 (itemHeader.length+ bpw-1)/bpw*bpw --word boundary--;
continue _ proc[itemHeader, GVStream, procData];
IF (itemHeader.type = LastItem) OR (NOT continue) THEN EXIT;
GVStream.SetIndex[nextItemIndex];
ENDLOOP;
};
bpw: INT = Basics.bytesPerWord;
ReceiveRName: PUBLIC PROC[GVStream: STREAM] RETURNS [ROPE] = {
ENABLE GVProtocol.Failed =>
IF why = protocolError THEN
ERROR SyntaxError[Rope.Concat["failed to read RName: ", text]];
RETURN[GVProtocol.ReceiveRName[GVStream]]; };
ReceiveCount: PUBLIC PROC[GVStream: STREAM] RETURNS [CARDINAL] = {
ENABLE GVProtocol.Failed =>
IF why = protocolError THEN
ERROR SyntaxError[Rope.Concat["failed to read count: ", text]];
RETURN[LOOPHOLE[GVProtocol.ReceiveCount[GVStream]]]; };
SyntaxError: PUBLIC ERROR [reason: ROPE] ~ CODE;
PrintGVItem: PUBLIC ArpaSMTPSyntax.GVItemProc = {
out: STREAM ~ NARROW[procData];
BEGIN
ENABLE {
IO.EndOfStream => {out.PutRope["<<<unexpected EOS>>>\n"]; GOTO Return};
SyntaxError => {
out.PutRope["<<<syntax error: "]; out.PutRope[reason]; out.PutRope[">>>\n"];
GOTO Return; }; };
PutHeader: PROC [type: ROPE, raw: BOOL] = TRUSTED {
out.PutF["----- %g (%bB), %g bytes", [rope[type]], [integer[LOOPHOLE[itemHeader.type, CARDINAL]]], [integer[itemHeader.length]] ];
out.PutRope[IF raw THEN " (raw format) -----\n" ELSE " -----\n"];
IF itemHeader.length <= 0 THEN ERROR SyntaxError["length <= 0"]; };
PutRaw: PROC [] = { -- somewhat inefficient, but infrequently used
currentIndex: INT = itemStream.GetIndex[];
nBytesLeft: INT _ itemHeader.length;
itemRestrictedStream: STREAM = ArpaSMTPSupport.CreateSubrangeStream[itemStream, currentIndex, currentIndex + nBytesLeft];
buffer: REF TEXT = RefText.ObtainScratch[100];
WHILE nBytesLeft > 0 DO
nBytesRead: INT _ itemRestrictedStream.GetBlock[
buffer, 0, MIN[nBytesLeft, buffer.maxLength]];
IF nBytesRead = 0 THEN ERROR IO.EndOfStream[itemRestrictedStream];
out.PutBlock[buffer, 0, nBytesRead];
nBytesLeft _ nBytesLeft - nBytesRead;
ENDLOOP;
out.PutChar['\n];
RefText.ReleaseScratch[buffer]; };
SELECT itemHeader.type FROM
PostMark => {
PutHeader["PostMark", FALSE];
out.PutRope[GVBasics.RopeFromTimestamp[GVProtocol.ReceiveTimestamp[itemStream]]];
out.PutChar['\n];};
Sender => {
PutHeader["Sender", FALSE];
out.PutRope[ReceiveRName[itemStream]];
out.PutChar['\n];};
ReturnTo => {
PutHeader["ReturnTo", FALSE];
out.PutRope[ReceiveRName[itemStream]];
out.PutChar['\n];};
Recipients => {
numRecips: INT = ReceiveCount[itemStream];
PutHeader["Recipients", FALSE];
THROUGH [1..numRecips] DO
out.PutRope[ReceiveRName[itemStream]]; out.PutChar['\n] ENDLOOP;};
Text => {PutHeader["Text", FALSE]; PutRaw[]};
Capability => {PutHeader["Capability", TRUE]; PutRaw[]};
Audio => {PutHeader["Audio", TRUE]; out.PutRope["<probably not worth printing>\n"]};
LastItem => {PutHeader["LastItem", FALSE]; out.PutChar['\n]};
ENDCASE => {PutHeader["", TRUE]; PutRaw[]};
EXITS Return => NULL;
END; };
BlessReturnPath: PUBLIC PROC [raw: ROPE] RETURNS [arpa: ROPE] =
BEGIN
length: INT = Rope.Length[raw];
host: ROPE;
IF Rope.Fetch[raw, 0] = '@ THEN { -- @Foo:X@Y case
FOR i: INT IN [1..length) DO
char: CHAR = Rope.Fetch[raw, i];
SELECT char FROM
',, ': => { host _ Rope.Substr[raw, 1, i-1]; EXIT; };
ENDCASE => NULL;
REPEAT FINISHED =>
ArpaSMTPSupport.Log[important, "Invalid syntax in return path: ", raw];
ENDLOOP; }
ELSE { --Foo@Bar
FOR i: INT DECREASING IN [0..length) DO
c: CHAR = Rope.Fetch[raw, i];
IF c = '@ THEN { host _ Rope.Substr[raw, i + 1, (length-i-1)]; EXIT; };
ENDLOOP; };

SELECT TRUE FROM
(host = NIL) => NULL;
~CheckHostName[host] =>
ArpaSMTPSupport.Log[important, "Invalid character in first return return host: ", raw];
ENDCASE => NULL;
SELECT TRUE FROM
Rope.Find[raw, ","] # -1 =>  {
};
ValidDomain[raw] =>
arpa _ raw;
raw.Fetch[0] = '@ => {
};
ENDCASE =>
BEGIN
length: INT _ Rope.Length[raw];
user, host, newHost: ROPE;
FOR i: INT DECREASING IN [0..length) DO
c: CHAR = raw.Fetch[i];
IF c = '@ THEN {
user _ Rope.Substr[raw, 0, i];
host _ Rope.Substr[raw, i + 1, (length-i-1)];
IF ~Rope.IsEmpty[host] THEN 
IF host.Fetch[0] = '[ THEN host _ Rope.Cat[host, ".ARPA"]; -- [36,1,2,6]
newHost _ NormalizeName[host];
IF Rope.IsEmpty[newHost] THEN {
ArpaSMTPSupport.Log[important, "Bogus return path: ", raw];
}
ELSE
arpa _ Rope.Cat[user, "@", newHost];
EXIT; };
REPEAT FINISHED => arpa _ raw;
ENDLOOP;
END;
IF arpa = NIL THEN arpa _ raw;
IF arpa # raw THEN ArpaSMTPSupport.Log[important, "Return path fixup: ", raw, " => ", arpa];
END;
ValidDomain: PROC [raw: ROPE] RETURNS [BOOLEAN] = {
FOR list: LIST OF Rope.ROPE _ ArpaConfig.validDomains, list.rest UNTIL list = NIL DO
domain: Rope.ROPE _ list.first;
IF DotTailed[raw, domain] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];};
UnBlessReturnPath: PUBLIC PROC [raw: ROPE] RETURNS [arpa: ROPE] =
BEGIN
IF raw = NIL THEN RETURN[NIL];
IF Rope.Fetch[raw, 0] #'" THEN RETURN[raw];
IF ~Tailed[raw, ArpaConfig.ourLocalName] THEN RETURN[raw];
RETURN[Rope.Substr[raw, 1, Rope.Length[raw]-3-Rope.Length[ArpaConfig.ourLocalName]]];
END;
ReversePath: PUBLIC PROC [gv: ROPE] RETURNS [arpa: ROPE] =
BEGIN
length: INT _ gv.Length[];
FOR i: INT DECREASING IN [0..length) DO
c: CHAR = gv.Fetch[i];
IF c = '\" THEN { -- "Foo" or "Foo".OSBUNorth
IF gv.Fetch[0] # '\" THEN EXIT;
IF i = length-1 THEN gv _ Rope.Substr[gv, 1, length-2]
ELSE gv _ Rope.Cat[Rope.Substr[gv, 1, i-1], Rope.Substr[gv, i+1, length-i-1]];
EXIT; };
IF c = '@ THEN {
glue: ROPE = IF gv.Fetch[0] = '@ THEN "," ELSE ":";
arpa _ Rope.Cat["@", ArpaConfig.ourLocalName, glue, gv];
RETURN; };
ENDLOOP;
gv _ FixupSpaces[gv];
gv _ MaybeAddQuotes[gv];
arpa _ Rope.Cat[gv, "@", ArpaConfig.ourLocalName]; -- Normal GV case
END;
HostAndUser: PUBLIC PROC [raw: ROPE] RETURNS [host, user: ROPE] = {
FOR list: LIST OF ROPE _ arpaSpecialRegistries, list.rest UNTIL list = NIL DO
raw _ FixupTail[raw, Rope.Cat[".", list.first]];
raw _ FixupTail[raw, Rope.Cat[".", list.first, ".ARPA"]];
ENDLOOP; 

raw _ FixupTail[raw, ".ARPA.ARPA"]; 

FOR list: LIST OF Rope.ROPE _ ArpaConfig.validDomains, list.rest UNTIL list = NIL DO
domain: Rope.ROPE _ list.first;
rope: Rope.ROPE _ Rope.Cat[".", domain, ".ARPA"];
raw _ TruncateTail[raw, rope];
ENDLOOP;

IF Rope.Find[raw, "].ARPA", 0, FALSE] # -1 THEN raw _ TruncateTail[raw, ".ARPA"];

IF raw.Fetch[0] # '@ THEN {
raw _ Redirect[raw, ".BITNET", ArpaConfig.bitnetGateway];
raw _ Redirect[raw, ".CSNet", ArpaConfig.csnetGateway];
raw _ Redirect[raw, ".UUCP", ArpaConfig.uucpGateway] };

IF raw.Fetch[0] # '@ THEN {

raw _ StripTail[raw, Rope.Cat["@", ArpaConfig.ourLocalName]];
raw _ StripTail[raw, Rope.Cat["@", ConvertExtras.RopeFromArpaAddress[ArpaConfig.ourLocalAddress]]];
raw _ StripTail[raw, Rope.Cat["@", ConvertExtras.RopeFromArpaAddress[ArpaConfig.ourLocalAddress], ".ARPA"]];


FOR list: LIST OF ROPE _ possibleOldNames, list.rest UNTIL list = NIL DO
raw _ StripTail[raw, Rope.Cat["@", list.first, ".ARPA"]];
raw _ StripTail[raw, Rope.Cat["@", list.first, ".COM"]];
raw _ StripTail[raw, Rope.Cat["@", list.first]];
ENDLOOP; 
}
ELSE {
raw _ StripHead[raw, Rope.Cat["@", ArpaConfig.ourLocalName]];
raw _ StripHead[raw, Rope.Cat["@", ConvertExtras.RopeFromArpaAddress[ArpaConfig.ourLocalAddress]]];
raw _ StripHead[raw, Rope.Cat["@", ConvertExtras.RopeFromArpaAddress[ArpaConfig.ourLocalAddress], ".ARPA"]];

FOR list: LIST OF ROPE _ possibleOldNames, list.rest UNTIL list = NIL DO
raw _ StripHead[raw, Rope.Cat["@", list.first, ".ARPA"]];
raw _ StripHead[raw, Rope.Cat["@", list.first, ".COM"]];
raw _ StripHead[raw, Rope.Cat["@", list.first]];
ENDLOOP; 

};
raw _ StripQuotes[raw];
[host: host, user: user] _ FindHostName[raw];
IF host = NIL THEN {
user _ StripTail[user, ".ARPA"]; -- Hack for testing by sending to Foo.PA.Arpa
user _ ForceRegistry[user];
user _ FixupUnderbars[user]; }; };
Redirect: PROC [old, domain, relay: ROPE] RETURNS [new: ROPE] =
BEGIN
tail: ROPE _ Rope.Cat[domain, ".ARPA"];
IF Tailed[old, tail] THEN old _ StripTail[old, ".ARPA"];
IF Tailed[old, domain] THEN {
length: INT;
old _ StripTail[old, domain];
length _ Rope.Length[old];
FOR i: INT DECREASING IN [0..length) DO
IF Rope.Fetch[old, i] = '@ THEN {
name: ROPE _ Rope.Substr[old, 0, i];
name _ StripQuotes[name]; -- "Joe User"@Host.xx
old _ Rope.Cat[name, "%", Rope.Substr[old, i + 1, (length-i-1)], domain];
old _ MaybeAddQuotes[old]; -- "Joe User%Host.xx"
EXIT; };
ENDLOOP;
old _ Rope.Cat[old, "@", relay]; };
RETURN[old];
END;


StripQuotes: PROC [old: ROPE] RETURNS [new: ROPE] = {
length: INT _ old.Length[];
new _ old;
IF length < 2 THEN RETURN;
IF old.Fetch[0] # '\" THEN RETURN;
SELECT TRUE FROM
old.Fetch[length-1] = '\" => new _ old.Substr[1, length-1-1];
Tailed[old, "\".ARPA"] => new _ old.Substr[1, length-1-6];
ENDCASE => RETURN;
length _ new.Length[];
FOR i: INT IN [0..length) DO
IF new.Fetch[i] = '\\ THEN EXIT;
REPEAT FINISHED => RETURN; -- No \ inside the string
ENDLOOP;
BEGIN
quoteSeen: BOOLEAN _ FALSE;
text: REF TEXT _ RefText.ObtainScratch[length];
FOR i: INT IN [0..length) DO
c: CHAR = new.Fetch[i];
IF c = '\\ AND ~quoteSeen THEN { quoteSeen _ TRUE; LOOP; };
text _ RefText.AppendChar[text, c];
quoteSeen _ FALSE;
ENDLOOP;
new _ Rope.FromRefText[text];
RefText.ReleaseScratch[text];
END; };
MaybeAddQuotes: PROC [old: ROPE] RETURNS [new: ROPE] =
BEGIN
length: INT _ old.Length[];
new _ old;
IF Rope.IsEmpty[new] THEN RETURN;
IF Rope.Fetch[new, 0] = '" THEN RETURN; -- Assume already quoted correctly
FOR i: INT IN [0..length) DO
SELECT Rope.Fetch[new, i] FROM
> 177C => EXIT; -- Funny characters. What should happen to these??
'(, '), '<, '>, '@, '<, ';, ':, '\\, '", '[, ']  => EXIT; -- Specials EXCEPT PERIOD!
'  => EXIT; -- Space
< 040C => EXIT; -- CTL
ENDCASE => NULL; -- Includes underbar
REPEAT FINISHED => RETURN; -- Nothing fancy inside the string
ENDLOOP;
new _ Rope.Cat["\"", old, "\""];
END;
FixupTail: PROC [old, tail: ROPE] RETURNS [new: ROPE] = {
new _ old;
IF Tailed[old, tail] THEN {
new _ StripTail[old, tail];
new _ Rope.Concat[new, ".ARPA"]; }; };
TruncateTail: PROC [old, tail: ROPE] RETURNS [new: ROPE] = {
new _ old;
IF Tailed[old, tail] THEN new _ StripTail[old, ".ARPA"]; };
Tailed: PROC [body, tail: ROPE] RETURNS [match: BOOL] = {
bodyLength: INT = body.Length[];
tailLength: INT = tail.Length[];
back: ROPE;
IF bodyLength <= tailLength THEN RETURN[FALSE];
back _ Rope.Substr[body, bodyLength-tailLength, tailLength];
IF Rope.Equal[back, tail, FALSE] THEN RETURN[TRUE];
RETURN[FALSE]; };

DotTailed: PROC [body, tail: ROPE] RETURNS [match: BOOL] = {
IF ~Tailed[body, tail] THEN RETURN[FALSE];
IF Rope.Fetch[body, Rope.Length[body]-Rope.Length[tail]-1] # '. THEN RETURN[FALSE];
RETURN[TRUE]; };


StripTail: PROC [body, tail: ROPE] RETURNS [new: ROPE] = {
bodyLength: INT = body.Length[];
tailLength: INT = tail.Length[];
IF ~Tailed[body, tail] THEN RETURN[body];
RETURN[Rope.Substr[body, 0, bodyLength - tailLength]]};
Headed: PROC [body, head: ROPE] RETURNS [match: BOOL] = {
bodyLength: INT = body.Length[];
headLength: INT = head.Length[];
char: CHAR;
IF bodyLength <= headLength+1 THEN RETURN[FALSE];
char _ Rope.Fetch[body, headLength];
IF char = ', OR char = ': THEN {
front: ROPE _ Rope.Substr[body, 0, headLength];
IF Rope.Equal[front, head, FALSE] THEN RETURN[TRUE]; };
RETURN[FALSE]; };
StripHead: PROC [body, head: ROPE] RETURNS [new: ROPE] = {
bodyLength: INT = body.Length[];
headLength: INT = head.Length[];
IF ~Headed[body, head] THEN RETURN[body];
RETURN[Rope.Substr[body, headLength+1, bodyLength - headLength -1]]};
FindHostName: PROC [raw: ROPE] RETURNS [host, user: ROPE] = {
length: INT _ raw.Length[];
newHost: ROPE;
user _ raw;
IF Rope.Fetch[raw, 0] = '@ THEN { -- @Foo:X@Y case
FOR i: INT IN [1..length) DO
char: CHAR = Rope.Fetch[raw, i];
SELECT char FROM
',, ': => {
host _ Rope.Substr[raw, 1, i-1];
IF ~CheckHostName[host] THEN RETURN["Invalid character in host name", raw];
newHost _ NormalizeName[host];
IF ~Rope.IsEmpty[newHost] THEN host _ newHost;
user _ Rope.Substr[raw, i + 1, (length-i-1)];
user _ Rope.Cat["@", host, Rope.FromChar[char], user];
RETURN; };
ENDCASE => NULL;
REPEAT FINISHED => RETURN["Invalid Syntax", raw];
ENDLOOP; };
FOR i: INT DECREASING IN [0..length) DO
c: CHAR = raw.Fetch[i];
IF c = '\" THEN EXIT;
IF c = '@ THEN { --Foo@Bar
user _ Rope.Substr[raw, 0, i];
host _ Rope.Substr[raw, i + 1, (length-i-1)];
IF ~CheckHostName[host] THEN RETURN["Invalid character in host name", raw];
newHost _ NormalizeName[host];
IF ~Rope.IsEmpty[newHost] THEN host _ newHost;
user _ MaybeAddQuotes[user]; -- "Foo Foo"@Bar
user _ Rope.Cat[user, "@", host];
RETURN; };
ENDLOOP;
FOR i: INT DECREASING IN [0..length) DO
c: CHAR = raw.Fetch[i];
IF c = '\" THEN EXIT;
IF c = '% THEN {  -- Hackery: foo%bar
user _ Rope.Substr[raw, 0, i];
host _ Rope.Substr[raw, i + 1, (length-i-1)];
IF ~CheckHostName[host] THEN RETURN["Invalid character in host name", raw];
newHost _ NormalizeName[host];
IF ~Rope.IsEmpty[newHost] THEN host _ newHost;
user _ MaybeAddQuotes[user];
user _ Rope.Cat[user, "@", host];
RETURN; };
ENDLOOP;
host _ NIL; };  -- No @, Must be GV
CheckHostName: PROC [host: ROPE] RETURNS [ok: BOOLEAN] =
BEGIN
length: INT _ Rope.Length[host];
FOR i: INT IN [1..length) DO
char: CHAR = Rope.Fetch[host, i];
SELECT char FROM
'(, '), '<, '>, '@, '<, ';, ':, '\\, '" => RETURN[FALSE]; -- Specials EXCEPT PERIOD and []!
'  => RETURN[FALSE]; -- Space
< 040C => RETURN[FALSE]; -- CTL
ENDCASE => NULL;
ENDLOOP;
RETURN[TRUE];
END;
NormalizeName: PUBLIC PROC [raw: ROPE] RETURNS [host: ROPE] =
BEGIN
IF Rope.IsEmpty[raw] THEN RETURN[NIL];
raw _ StripTail[raw, ".ARPA"];  -- fix foo.EDU.ARPA case
IF raw.Fetch[0] = '[ THEN 
IF ~DotTailed[raw, "ARPA"] THEN RETURN[Rope.Cat[raw, ".ARPA"]]; 
IF Rope.Find[raw, ".",, FALSE] = -1 THEN  raw _ Rope.Cat[raw, ".ARPA"]; 
IF SpecialDomain[raw] THEN RETURN[raw];
host _ ArpaName.AliasToName[raw, ArpaConfig.resolv^].name;
RETURN[host];
END;
SpecialDomain: PROC [raw: ROPE] RETURNS [BOOLEAN] = {
FOR list: LIST OF Rope.ROPE _ ArpaConfig.specialDomains, list.rest UNTIL list = NIL DO
domain: Rope.ROPE _ list.first;
IF DotTailed[raw, domain] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];};



ForceRegistry: PROC [raw: ROPE] RETURNS [user: ROPE] =
BEGIN
length: INT _ raw.Length[];
user _ raw;
FOR i: INT DECREASING IN [0..length) DO
IF raw.Fetch[i] = '. THEN RETURN;
ENDLOOP;
user _ Rope.Concat[raw, ArpaSMTPControl.defaultRegistry];
END;
FixupSpaces: PROC [raw: ROPE] RETURNS [user: ROPE] =
BEGIN
length: INT _ raw.Length[];
user _ raw;
FOR i: INT IN [0..length) DO
IF raw.Fetch[i] = '  THEN EXIT;
REPEAT FINISHED => RETURN;
ENDLOOP;
user _ Rope.Translate[base: user, translator: SpaceToUnderbar];
END;

FixupUnderbars: PROC [raw: ROPE] RETURNS [user: ROPE] =
BEGIN
length: INT _ raw.Length[];
name, registry: ROPE;
user _ raw;
FOR i: INT IN [0..length) DO
IF raw.Fetch[i] = '_ THEN EXIT;
REPEAT FINISHED => RETURN;
ENDLOOP;
FOR i: INT DECREASING IN [0..length) DO
IF raw.Fetch[i] = '. THEN {
name _ Rope.Substr[raw, 0, i];
registry _ Rope.Substr[raw, i, (length-i)]; -- Registry includes the dot
EXIT; };
REPEAT FINISHED => name _ user; -- No registry (?)
ENDLOOP;
name _ Rope.Translate[base: name, translator: UnderbarToSpace];
user _ Rope.Cat["\"", name, "\"", registry];
END;

UnderbarToSpace: PROC [old: CHAR] RETURNS [new: CHAR] =
BEGIN
IF old = '_ THEN RETURN[' ] ELSE RETURN[old];
END;

SpaceToUnderbar: PROC [old: CHAR] RETURNS [new: CHAR] =
BEGIN
IF old = '  THEN RETURN['_] ELSE RETURN[old];
END;
END.
�����ArpaSMTPSyntaxImpl.mesa
Copyright c 1985 by Xerox Corporation.  All rights reserved.
Last Edited by: HGM, May 8, 1985 0:30:18 am PDT
Last Edited by: DCraft, November 22, 1983 1:59 pm
Last Edited by: Taft, February 3, 1984 1:17:33 pm PST
Hal Murray July 2, 1985 2:31:00 am PDT
John Larson, March 13, 1988 6:14:14 pm PST
Beware of bounds fault - count is a NAT
1) Bitch if name of first host on return path isn't recognized by name servers
2) Make sure it ends in .ARPA (or such) so GV will send rejections back via us
ArpaName.AliasToName[host].status = bogus => {
ArpaSMTPSupport.Log[important, "BOGUS host name in return path: ", host]; };
@A,@B:User@Host => comma would be bogus in rejection msgs
arpa _ Rope.Cat["\"", raw, "\"@", ArpaConfig.ourLocalName];
User@Host.ARPA or @Mumble:User@Host.ARPA
Can't fixup tail of name: it might be in a different name space
arpa _ Rope.Cat["\"", raw, "\"@", ArpaConfig.ourLocalName];
User@Host (no .ARPA)
Somebody fed us an alias rather than the truth. Normalize it.
Yetch. Somebody fed us a bogus name. 
Rejection msgs from GV will probably not work.
arpa _ Rope.Cat["\"", raw, "\"@", ArpaConfig.ourLocalName]; 
Strip "..."@Xerox.ARPA to be kind to other mailers
(the ones that aren't bright enough to process quotes)
Hackery to translate user@host.CSNet into user%host@relay.cs.net
Beware: There is similar code in MTTReeOpsImpl
Without the next lines, mail to ourselves will go around again!!! Good for testing, but..

Without the next line, mail to ourselves will go around again!!! Good for testing, but..

See pg 10 of RFC 822. An Atom is anything except specials, SPACE, and CTLs.
This won't do the right things with Foo..bar
NB: Remove following , or : too
No quote checking on this path
Ê	��˜�head™Icodešœ
Ïmœ1™<Jšœ/™/Jšœ1™1Jšœ5™5L™&L™*code2šÏk	˜	Mšœžœq˜Mšœ	žœ˜Mšœžœ˜(Mšœžœ˜2Mšœ"˜"Mšœžœ˜Jšœžœ˜*Mšœ	žœ,˜:MšœžœK˜[MšžœžœOžœ˜_Mšœžœ-˜:MšœžœKžœ˜n——šÐlnœž
˜!Mšžœ^žœ˜vMšžœ˜Mšž˜Mšžœžœžœžœ˜Mšžœžœžœ˜J˜�Jš	œžœžœžœžœ4˜YJš	œžœžœžœžœ!˜KJ˜�šÏnœžœžœžœ.žœžœžœ˜oMšœžœ˜Mšœ
žœ˜M˜ šž˜M˜4MšœJÏcœ˜\Mšœ0˜0Mš
žœžœžœžœžœ˜<M˜!Mšž˜—M˜—Mšœžœ˜š œžœžœžœžœžœ˜>šžœ˜šžœž˜Mšžœ:˜?——Mšžœ'˜-—š œžœžœžœžœžœ˜Bšžœ˜šžœž˜Mšžœ:˜?——Mšžœžœ(˜7—Mš	œ
žœžœ
žœžœ˜0š œžœ˜1Mšœžœžœ˜Mšž˜šžœ˜Mšžœ8žœ	˜Gšœ˜MšœL˜LMšžœ˜——š
 	œžœžœžœžœ˜3Mšœ<žœžœ$˜‚Mšœžœžœžœ
˜AMšžœžœžœ˜C—š œžœ¡.˜BMšœžœ˜*Mšœžœ˜$Mšœžœ]˜yMšœžœžœ˜.šžœž˜Mšœ'™'šœžœ!˜0Mšœžœ ˜.—Mšžœžœžœžœ#˜BM˜$M˜%Mšžœ˜—M˜M˜"—šžœž˜šœ
˜
Mšœžœ˜MšœQ˜QMšœ˜—šœ˜Mšœžœ˜Mšœ&˜&Mšœ˜—šœ
˜
Mšœžœ˜Mšœ&˜&Mšœ˜—˜Mšœžœ˜*Mšœžœ˜šžœž˜Mšœ8žœ˜B——Mšœžœ
˜-Mšœ'žœ
˜8Mšœžœ3˜TMšœ#žœ˜=Mšžœžœ
˜+—Mšžœž˜Mšžœ˜—š œžœžœžœžœžœ˜?Mšž˜M™NM™NMšœžœ˜Mšœžœ˜šžœžœ¡˜2šžœžœžœ
ž˜Mšœžœ˜ šžœž˜Mšœ-žœ˜5Mšžœžœ˜—šžœžœ˜MšœG˜G—Mšžœ˜
——šžœ¡	˜š	žœžœž
œžœ
ž˜'Mšœžœ˜Mšžœžœ1žœ˜GMšžœ˜M˜�——šžœžœž˜Mšžœžœžœ˜šœ˜MšœW˜W—šœ.™.MšœL™L—Mšžœžœ˜—šžœžœž˜šœ˜Mšœ9™9Mšœ;™;M˜—šœ˜M™(Mšœ˜—šœ˜M™?Mšœ;™;M˜—šžœ˜
M™M™=Mšž˜Mšœžœ˜Mšœžœ˜š	žœžœž
œžœ
ž˜'Mšœžœ˜šžœžœ˜M˜M˜-šžœž˜Mšžœžœ!¡
˜H—Mšœ˜šžœžœ˜Mšœ%™%Mšœžœ™.Mšœ;˜;Mšœ<™<Mšœ˜—šž˜Mšœ$˜$—Mšžœ˜—Mšžœžœ˜Mšžœ˜—Mšžœ˜——Mšžœžœžœ˜MšžœžœJ˜\Mšžœ˜—š
 œžœžœžœžœ˜3š
žœžœžœžœ&žœžœž˜TJšœ
žœ˜Jšžœžœžœžœ˜,Jšžœ˜—Jšžœžœ˜—š œžœžœžœžœžœ˜AMšž˜M™2M™6Mš
žœžœžœžœžœ˜Mšžœžœžœ˜+Mšžœ'žœžœ˜:MšžœO˜UMšžœ˜—š œžœžœžœžœžœ˜:Mšž˜Mšœžœ˜š	žœžœž
œžœ
ž˜'Mšœž	œž˜šžœ	žœ¡˜-Mšžœžœžœ˜Mšžœžœ"˜6MšžœJ˜NMšžœ˜—šžœžœ˜Mš	œžœžœžœžœ˜3Mšœ8˜8Mšžœ˜
—Mšžœ˜—Mšœ˜Mšœ˜Mšœ3¡˜DMšžœ˜—š œžœžœžœžœžœ˜Cš
žœžœžœžœ$žœžœž˜MMšœ0˜0Mšœ9˜9Lšžœ˜	—M˜�Mšœ$˜$M˜�š
žœžœžœžœ&žœžœž˜TJšœ
žœ˜Jšœžœ"˜1Mšœ˜Jšžœ˜J˜�—Mšžœžœžœ"˜QM˜�šžœžœ˜Mšœ@™@M™.Mšœ9˜9Mšœ7˜7Mšœ7˜7M˜�—šžœžœ˜MšœY™YL˜�—Mšœ=˜=Mšœc˜cMšœl˜lM˜�L˜�š
žœžœžœžœžœžœž˜HMšœ9˜9Mšœ8˜8Mšœ0˜0Lšžœ˜	M™�Mšœ˜—šžœ˜MšœX™XM™�Mšœ=˜=Mšœc˜cMšœl˜lM˜�š
žœžœžœžœžœžœž˜HMšœ9˜9Mšœ8˜8Mšœ0˜0Lšžœ˜	—M˜�Mšœ˜—Mšœ˜Mšœ-˜-šžœžœžœ˜MšœN˜NMšœ˜Mšœ"˜"——š
 œžœžœžœžœ˜?Mšž˜Mšœžœ˜'Mšžœžœ˜8šžœžœ˜Mšœžœ˜Mšœ˜Mšœ˜šžœžœž
œ
ž˜'šžœžœ˜!Mšœ$˜$M˜/MšœI˜IMšœ0˜0Mšžœ˜—Mšžœ˜—Mšœ#˜#—Mšžœ˜Mšžœ˜M˜�M˜�—š
 œžœžœžœžœ˜5Mšœžœ˜M˜
Mšžœžœžœ˜Mšžœžœžœ˜"šž˜Mšœžœ!˜=Mšœ:˜:Mšžœžœ˜—Mšœ˜šžœžœžœ
ž˜Mšžœžœžœ˜ Mšžœžœžœ¡˜4Mšžœ˜—Mšž˜Mšœžœžœ˜Mšœžœžœ!˜/šžœžœžœ
ž˜Mšœžœ˜Mš
žœ	žœžœžœžœ˜;Mšœ#˜#Mšœ	ž	˜Mšžœ˜—Mšœ˜Mšœ˜Mšžœ˜—š
 œžœžœžœžœ˜6Mšž˜Mšœ
žœ+žœ™KM™,Mšœžœ˜M˜
Mšžœžœžœ˜!Mšžœžœžœ¡"˜Jšžœžœžœ
ž˜šžœž˜Mšœ
žœ¡2˜BMšœ4žœ¡˜TMšœžœ¡˜Mšœ
žœ¡˜Mšžœžœ¡˜%—Mšžœžœžœ¡"˜=Mšžœ˜—M˜ Mšžœ˜—š
 	œžœ
žœžœžœ˜9Mšœ
˜
šžœžœ˜Mšœ˜Mšœ&˜&——š
 œžœ
žœžœžœ˜<Mšœ
˜
Mšžœžœ"˜;—š
 œžœ
žœžœ	žœ˜9Mšœžœ˜ Mšœžœ˜ Mšœžœ˜Mšžœžœžœžœ˜/Mšœ<˜<Mš
žœžœžœžœžœ˜3Mšžœžœ˜M˜�—š
 	œžœžœžœ	žœ˜<Jšžœžœžœžœ˜*Jšžœ>žœžœžœ˜SJšžœžœ˜J˜�M˜�—š
 	œžœžœžœžœ˜:Mšœžœ˜ Mšœžœ˜ Mšžœžœžœ˜)Mšžœ1˜7—š
 œžœ
žœžœ	žœ˜9Mšœžœ˜ Mšœžœ˜ Mšœžœ˜Mšžœžœžœžœ˜1Mšœ$˜$šžœžœžœ˜ Mšœžœ$˜/Mš
žœžœžœžœžœ˜7—Mšžœžœ˜—š
 	œžœžœžœžœ˜:Mšœžœ˜ Mšœžœ˜ Mšžœžœžœ˜)M™Mšžœ?˜E—š
 œžœžœžœžœ˜=Mšœžœ˜Mšœ	žœ˜M˜šžœžœ¡˜2šžœžœžœ
ž˜Mšœžœ˜ šžœž˜šœ˜M˜ Mšžœžœžœ(˜KMšœ˜Mšžœžœ˜.M˜-Mšœ™Mšœ6˜6Mšžœ˜
—Mšžœžœ˜—Mšžœžœžœ˜1Mšžœ˜——š	žœžœž
œžœ
ž˜'Mšœž	œž˜Mšžœ	žœžœ˜šžœžœ¡	˜M˜M˜-Mšžœžœžœ(˜KMšœ˜Mšžœžœ˜.Mšœ¡˜-M˜!Mšžœ˜
—Mšžœ˜—š	žœžœž
œžœ
ž˜'Mšœž	œž˜Mšžœ	žœžœ˜šžœžœ¡˜%M˜M˜-Mšžœžœžœ(˜KMšœ˜Mšžœžœ˜.M˜M˜!Mšžœ˜
—Mšžœ˜—MšœžœÏiÐci˜#—š
 
œžœžœžœžœ˜8Mšž˜Mšœžœ˜ šžœžœžœ
ž˜Mšœž	œž˜!šžœž˜Mšœ+žœžœžœ¡!˜[Mšœžœžœžœ¡˜Mšœ
žœžœžœ¡˜Mšžœžœ˜—Mšžœ˜—Mšžœžœ˜
Mšžœ˜—š
 
œžœžœžœžœ˜=Mšž˜Mšžœžœžœžœ˜&Mšœ ¡˜8šžœžœ˜Mšžœžœžœ˜@—Mšžœžœžœ ˜HMšžœžœžœ˜'Mšœ:˜:Mšžœ˜
Mšžœ˜—š
 
œžœžœžœžœ˜5š
žœžœžœžœ(žœžœž˜VJšœ
žœ˜Jšžœžœžœžœ˜,Jšžœ˜—Jšžœžœ˜J˜�J˜�—M˜�š
 
œžœžœžœžœ˜6Mšž˜Mšœžœ˜M˜šžœžœž
œ
ž˜'Mšžœ
žœ˜!Mšžœ˜—Mšœ9˜9Mšžœ˜—š
 œžœžœžœžœ˜4Mšž˜Mšœžœ˜M˜šžœžœ
ž˜Mšžœžœžœ˜Mšžœžœžœ˜Mšžœ˜—Mšœ?˜?Mšžœ˜—J˜�š
 œžœžœžœžœ˜7Mšž˜Mšœžœ˜Mšœž˜M˜šžœžœ
ž˜Mšžœžœžœ˜Mšžœžœžœ˜Mšžœ˜—šžœžœž
œ
ž˜'šžœ
ž˜Mšœ˜Mšœ,¡˜HMšžœ˜—Mšžœžœ#˜2Mšžœ˜—Mšœ?˜?Mšœ,˜,Mšžœ˜—J˜�š
 œžœžœžœžœ˜7Jšž˜Jš
žœ
žœžœžœžœ˜-Jšžœ˜J˜�—š
 œžœžœžœžœ˜7Jšž˜Jš
žœ
žœžœžœžœ˜-Jšžœ˜—Mšžœ˜——�…—����A®��_…��