1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111 |
- (* Aubrey McIntosh, Ph.D. Jan 21, 2003
- * This code may be distributed under the same terms and conditions as the Bluebottle operating system
- * from ETH, Zürich.
- *
- * RFC 821 Receiver module. Listen on port 25, put messages and envelopes into files.
- *
- * Hand modified Coco-R output.
- * The scanner differs from the Coco provided scanner.
- *
- * This code is a result of hand merging a working but poorly designed prototype,
- * AosSMTPReceiver1.Mod, and the Coco output.
- *
- * Jan 21, 2003 21:53 CST: The merge is essentially complete, the scanner has not been
- * written, a code walk through has not been done. i.e. major omissions may exist.
- * Jan 23, 2003 16:35 CST: Accepted first message.
- * Jan 25, 2003 20:00: Have log files. Capture raw data sendmail sends here.
- * Jan 26, 2003 20:00: Place onto alternate path: FAT:/Mail/<message name>
- * Feb 1, 2003 0.1.28 Add Logging: flood of connections from 160.94.128.45
- * Feb 11, 2003 0.1.29 Test whether DNS extension is on name when writing message id.
- * Feb 18, 2003 0.1.33 Limit total number of connections. Place diagnostics in log.
- * accept core.inf.ethz.ch always.
- * Feb 19, 2003 0.1.34 Add diagnostic test for EOF condition.
- * 0.1.38 Fix error where 1st of many messages is dropped (overwritten?)
- * Feb 23, 2003
- * 0.1.40 Reconcile behavior of emwac and the .Rcp file format.
- * Mar 16, 2003
- * 0.1.41 Make file name initialization more robust.
- * Mar 31, 2003 The program seems to hang if the remote sender does not issue QUIT.
- * Jan 3, 2004 Name changed to AlmSMTPReceiver for release to BlueBottle community.
- * Changes 0.1.42 appear lost in move.
- * Sept 21, 2004 Initialise config file on first execution. Use AOS filesystem names.
- (* Initial SMTP handshake *)
- BEGIN {EXCLUSIVE}
- TakeNumber;
- AdvanceNumber
- END;
- OpenLog;
- LOOP (*Parse SMTP*)
- OpenMail;
- IF Finished THEN EXIT END
- BEGIN {EXCLUSIVE}
- TakeNumber;
- AdvanceNumber
- END
- END
- *
- *
- * To Do:
- * Make robust. E.g. HALT is not good error recovery.
- * Integrate with Frey's Abstract Mail
- * Produce message and broadcast mechanism. E.g., for use in poping up display when VIP
- * sends message.
- *
- *)
- MODULE AlmSmtpReceiver;
- IMPORT DNS, Files, Streams, IP, Modules, KernelLog, TCP, TCPServices, Dates, Strings;
- CONST
- (* Some of the configurable items. *)
- AlmSmtpReceiverPort = 25; (* Well, semi-configurable. *)
- MaxActive = 3+1;
- ID = "BlueBottle Receiver ";
- Version = "MailBottle (0.2.00.16)";
- Rcp = ".Rcp";
- Msg = ".Msg";
- Log = ".Log";
- ConfigFileName = "mail.config";
- ToDisk = TRUE; (* Debug *)
- DebugMsg = FALSE;
- RcptInFileName = TRUE;
- MaxUserName = 11;
- Prefix = "In."; (*Administer must create this manually.*)
- AlwaysAccept = "129.132.178.196";
- (* End of these configurable items. *)
- (* Constants for the Scanner *)
- CONST
- EOF = 0X;
- maxLexLen = 127;
- noSym = 13;
- (* Types for the Scanner *)
- TYPE
- ErrorProc* = PROCEDURE (n: INTEGER);
- StartTable = ARRAY 128 OF INTEGER;
- (* Variables for the Scanner *)
- VAR
- errors*: INTEGER; (*number of errors detected*)
- lasterror* : INTEGER;
- charcount : LONGINT;
- getCalls : LONGINT;
- start: StartTable; (*start state for every character*)
- Pattern, Ack : ARRAY 6 OF CHAR;
- active : LONGINT;
- CONST
- maxP = 13;
- maxT = 13;
- nrSets = 3;
- setSize = 32; nSets = (maxT DIV setSize) + 1;
- SyEol = 1;
- SyCopy = 2;
- SyHelo =3;
- SyQuit =4;
- SyNoop =5;
- SyRset =6;
- SyData =7;
- SyDot =8;
- SyRcpt =9;
- SyTo =10;
- SyMail =11;
- SyFrom =12;
- SyTimeout = 14;
- Tab = 09X;
- LF = 0AX;
- CR = 0DX;
- TYPE
- SymbolSet = ARRAY nSets OF SET;
- TYPE
- String = ARRAY 128 OF CHAR;
- TokenPtr = POINTER TO Token;
- Token = RECORD s : String; next : TokenPtr END;
- EnvelopePtr = POINTER TO Envelope;
- Envelope = RECORD
- mta, revMta, from : String;
- to : TokenPtr;
- END;
- Message* = RECORD env* : EnvelopePtr; file* :Files.File; END;
- SmtpAgent* = OBJECT (TCPServices.Agent)
- VAR
- ch: CHAR; (*current input character*)
- res: WORD;
- out: Streams.Writer; in: Streams.Reader;
- log : Files.Writer;
- env : Envelope;
- thisName, verbSy : String;
- finished : BOOLEAN;
- sym: INTEGER; (* current input symbol *)
- state : INTEGER;
- badTokens : LONGINT;
- auxString : String;
- (* Support procedures *)
- PROCEDURE GetCh():CHAR;
- VAR ch : CHAR;
- BEGIN
- ch := in.Get();
- log.Char (ch); log.Update;
- RETURN ch
- END GetCh;
- PROCEDURE ConsumeName;
- BEGIN {EXCLUSIVE}
- COPY (nextName, thisName);
- UpdateName (nextName)
- END ConsumeName;
- PROCEDURE AvailableName;
- VAR
- name : String;
- msgFile: Files.File;
- BEGIN
- COPY (Prefix, name);
- AddExt (name, thisName);
- AddExt (name, Log);
- WHILE (Files.Old (name) # NIL)
- DO
- ConsumeName;
- COPY (Prefix, name);
- AddExt (name, thisName);
- AddExt (name, Log);
- msgFile := Files.Old (name);
- END;
- END AvailableName;
- PROCEDURE OpenLog; (*1 file per session. Name is same as when session opens, i.e., not agree w/ .Msg & .Rcp *)
- VAR
- msgFile: Files.File;
- name : String;
- BEGIN
- COPY (Prefix, name);
- AddExt (name, thisName);
- AddExt (name, Log);
- msgFile := Files.Old (name);
- ToLog0 ("before search."); KernelLog.Exit;
- WHILE msgFile # NIL
- DO
- ToLog0 ("during search."); KernelLog.String (name); KernelLog.Exit;
- ConsumeName;
- COPY (Prefix, name);
- AddExt (name, thisName);
- AddExt (name, Log);
- msgFile := Files.Old (name);
- END;
- ToLog0 ("after search."); KernelLog.Exit;
- msgFile := Files.New (name);
- Files.OpenWriter ( log, msgFile, 0);
- Files.Register (msgFile);
- END OpenLog;
- PROCEDURE ToMemory* (VAR token: ARRAY OF CHAR);
- VAR maxix, ix : LONGINT; trash, next : CHAR;
- BEGIN
- next := in.Peek();
- WHILE (next=" ") OR (next=Tab) DO trash := GetCh (); INC (charcount); next := in.Peek() END;
- maxix := LEN (token)-1;
- WHILE (next#" ") & (next#Tab) & (next#CR) & (next#LF)
- DO
- ch := GetCh (); INC (charcount); next := in.Peek(); (* Jan 23, 2003 v. 0.1.02 *)
- IF ix < maxix
- THEN
- token [ix] := ch;
- INC (ix)
- END
- END;
- token [ix] := 0X;
- Expect (SyCopy)
- END ToMemory;
- PROCEDURE DebugMsg1* (msg : ARRAY OF CHAR);
- BEGIN
- IF DebugMsg
- THEN
- out.String (msg);
- out.Ln;
- out.Update()
- END
- END DebugMsg1;
- PROCEDURE PutStatus1* (msg : ARRAY OF CHAR);
- BEGIN
- Confirm(SyEol); (*Expect is split to a Confirm / Get pair to let the output occur.*)
- out.String (msg);
- out.Ln;
- out.Update();
- Get
- END PutStatus1;
- PROCEDURE ChangeStatus1* (newsym : INTEGER; msg : ARRAY OF CHAR);
- BEGIN
- Confirm(SyEol);
- sym := newsym;
- out.String (msg);
- out.Ln;
- out.Update();
- END ChangeStatus1;
- PROCEDURE PutStatus2* (msg0, msg1 : ARRAY OF CHAR);
- BEGIN
- Confirm(SyEol); (*Expect is split to a Confirm / Get pair to let the output occur.*)
- out.String (msg0);
- out.String (msg1);
- out.Ln;
- out.Update; (* ignore out.res *)
- Get
- END PutStatus2;
- PROCEDURE ChangeStatus2* (newsym : INTEGER; msg0, msg1 : ARRAY OF CHAR);
- BEGIN
- Confirm(SyEol);
- sym := newsym;
- out.String (msg0);
- out.String (msg1);
- out.Ln;
- out.Update; (* ignore out.res *)
- END ChangeStatus2;
- PROCEDURE AddExt* ( VAR name : String; ext : ARRAY OF CHAR);
- VAR i, j, skipped : INTEGER;
- BEGIN
- i := 0;
- WHILE ( i < LEN(name)-1 ) & ~(name[i] < " ")
- DO
- INC (i)
- END;
- j := 0; skipped := 0;
- WHILE ( i+j < LEN(name)-1 ) & (j<LEN(ext)-1) & (ext[j] # 0X)
- DO
- IF (ext[j] = "<") OR (ext[j] = ">")
- THEN
- INC (j); INC (skipped)
- ELSE
- name[i+j-skipped] := ext[j];
- INC (j)
- END;
- END;
- name[i+j] := 0X
- END AddExt;
- PROCEDURE PutBareName ( name : String; VAR wr : Files.Writer );
- VAR ix : LONGINT; ch : CHAR;
- BEGIN
- ix := 0;
- WHILE (ix<LEN(name)) & (name[ix]#0X)
- DO
- ch := name [ix];
- IF (ch#"<") & (ch#">") THEN wr.Char (ch) END;
- INC (ix)
- END
- END PutBareName;
- PROCEDURE PutEnvelope ( (* not VAR! *) name : String );
- VAR envF : Files.File; ew : Files.Writer; to: TokenPtr;
- msgName, rcpPathName : String;
- BEGIN
- COPY (name, msgName);
- (*
- AddExt (msgName, "@");
- AddExt (msgName, NetSystem.hostName);
- AddExt (msgName, ".");
- AddExt (msgName, DNS.domain);
- *)
- COPY (Prefix, rcpPathName);
- AddExt (rcpPathName, name); (*alm 9/21/2004*)
- AddExt (rcpPathName, Rcp); (*Name with no prefix*)
- envF := Files.New (rcpPathName);
- (*A trap sometimes happens when here:
- Process: 354 run 0 3 01F159B0:AlmSmtpReceiver.SmtpAgent ATADisks.Interrupt.Wait pc=815 {}
- *)
- Files.OpenWriter ( ew, envF, 0);
- ew.String ("Message-ID: <");
- ew.String (msgName);
- (*
- ew.Char ("@");
- ew.String (DNS.domain);
- *)
- ew.Char (">");
- ew.Ln;
- ew.String ("Return-path: ");
- PutBareName (env.from, ew);
- ew.Ln;
- to := env.to;
- WHILE to # NIL DO
- ew.String ("Recipient: ");
- PutBareName (to.s, ew);
- to := to.next;
- ew.Ln;
- END;
- ew.Update;
- Files.Register (envF);
- END PutEnvelope;
- PROCEDURE UpdateName (VAR s : String);
- VAR i : INTEGER; ch : CHAR; carry : INTEGER;
- BEGIN
- i := 10; (* 10 digits significant in name *)
- carry := 1;
- WHILE (1<=i) & (carry = 1) DO
- ch := CHR (ORD(s[i]) + carry);
- IF '9' < ch
- THEN
- ch := "0";
- carry := 1
- ELSE
- carry := 0
- END;
- s[i] := ch;
- DEC (i)
- END
- END UpdateName;
- (* Begin Parser Productions *)
- PROCEDURE HELO*;
- VAR res : WORD;
- BEGIN
- Confirm(SyHelo);
- sym := SyCopy; ToMemory (env.mta);
- DNS.HostByNumber (SELF.client.fip, env.revMta, res);
- PutStatus2 ("250 Your email is welcome here, ", env.mta);
- END HELO;
- PROCEDURE RSET*;
- BEGIN
- Expect(SyRset);
- env.mta := ""; env.from := ""; env.to := NIL;
- PutStatus1 ("250 Requested mail action okay, completed.");
- END RSET;
- PROCEDURE NOOP*;
- BEGIN
- Expect(SyNoop);
- PutStatus1 ("250 Requested mail action okay, completed.");
- END NOOP;
- PROCEDURE QUIT*;
- BEGIN
- Expect(SyQuit);
- finished := TRUE;
- ChangeStatus1 (SyQuit, "221 Goodbye.."); (*Avoid executing another Get.*)
- client.Close();
- END QUIT;
- PROCEDURE RCPT*;
- VAR to : TokenPtr;
- BEGIN
- Expect(SyRcpt);
- Confirm(SyTo);
- NEW (to);
- sym := SyCopy; ToMemory (to.s);
- to.next := env.to; env.to := to;
- PutStatus2 ("250 Recipient okay: ", to.s);
- END RCPT;
- PROCEDURE Test;
- BEGIN
- IF in.Available() < 1
- THEN HALT( 44 )
- END
- END Test;
- PROCEDURE ToFile(name : String);
- VAR
- msg: Files.File;
- msgWr : Files.Writer;
- ix, testIx : LONGINT;
- receiveTime, remoteIP : String;
- PROCEDURE WriteIPNr( ip : IP.Adr );
- VAR result : WORD; str : ARRAY 128 OF CHAR;
- BEGIN
- IP.AdrToStr(ip, remoteIP);
- msgWr.String (" (");
- msgWr.String (remoteIP);
- DNS.HostByNumber (ip, str, result);
- msgWr.String (" --> ");
- IF result = DNS.Ok
- THEN
- msgWr.String (str)
- ELSE
- msgWr.String ("lookup failed.")
- END;
- msgWr.Char (")");
- END WriteIPNr;
- BEGIN
- AddExt (name, Msg);
- IF ToDisk THEN
- msg := Files.New (name);
- Files.OpenWriter ( msgWr, msg, 0);
- ToLog0 (name);
- KernelLog.Exit;
- Strings.FormatDateTime("www, dd mmm yyyy hh:nn:ss -0600 (CST)", Dates.Now(), receiveTime);
- msgWr.String ("Received: ");
- msgWr.Ln; msgWr.Char (Tab); msgWr.String ("from ");
- msgWr.String (env.mta);
- WriteIPNr(SELF.client.fip);
- msgWr.Ln; msgWr.Char (Tab); msgWr.String ("by ");
- msgWr.String (DNS.domain);
- WriteIPNr(SELF.client.int.localAdr);
- msgWr.Ln; msgWr.Char (Tab);
- msgWr.String ("with ");
- msgWr.String (Version);
- msgWr.String (" id "); msgWr.String (thisName);
- msgWr.Char ("@"); msgWr.String (DNS.domain);
- msgWr.Ln; msgWr.Char (Tab); msgWr.String ("for ");
- msgWr.String (env.to.s);
- msgWr.Char (Tab); msgWr.String ("; "); msgWr.String (receiveTime);
- msgWr.Ln
- END;
- ch := GetCh (); INC (charcount); (* Read first v 0.1.02 *)
- testIx := 0;
- LOOP
- IF in.res = Streams.EOF
- THEN
- ToLog0 ("EOF on input stream."); KernelLog.Exit; sym := SyEol; EXIT
- END;
- IF ch=Pattern[0]
- THEN
- LOOP
- ch := GetCh (); INC (charcount);
- testIx := 1;
- WHILE (testIx <= 4) & (ch=Pattern[testIx])
- DO
- IF testIx < 4
- THEN
- ch := GetCh ();
- INC (charcount);
- END;
- INC (testIx)
- END;
- IF DebugMsg
- THEN
- FOR ix := 0 TO testIx-1
- DO
- out.Char (Ack[ix])
- END;
- out.Update
- END;
- IF testIx=5
- THEN
- msgWr.Char (CR); msgWr.Char (LF);
- sym := SyEol; (*Have read both "." and CR/LF*)
- ELSE
- FOR ix := 0 TO testIx-1
- DO
- msgWr.Char (Pattern[ix])
- END;
- (* msgWr.Char (ch); *)
- (* testIx := 0 *)
- END;
- EXIT
- END;
- IF testIx=5 THEN EXIT END
- ELSE
- msgWr.Char (ch)
- END;
- IF testIx#0 THEN testIx := 0 (*Start test again at current character.*) ELSE ch := GetCh (); INC (charcount) END
- END ;
- IF DebugMsg THEN out.Char ("!"); out.Update END;
- IF ToDisk THEN msgWr.Update END;
- IF ToDisk THEN Files.Register (msg) END
- END ToFile;
- PROCEDURE DATA* (name : String);
- BEGIN
- Expect(SyData);
- ChangeStatus1 (SyCopy, "354 Send message now, end with CRLF . CRLF");
- sym := SyCopy; ToFile (name);
- Confirm(SyEol);
- END DATA;
- PROCEDURE AddUserToName (VAR thisName : String);
- VAR
- pos : INTEGER;
- BEGIN
- IF RcptInFileName
- THEN
- AddExt ( thisName, "."); (*Preparation for mailbox-in-name interpretation.*)
- pos := 0;
- WHILE (pos < LEN (thisName)) & (thisName [pos] # 0X) DO INC (pos) END;
- AddExt ( thisName, env.to.s); (*Preparation for mailbox-in-name interpretation.*)
- thisName [pos + MaxUserName] := 0X;
- WHILE (pos < LEN (thisName)) & (thisName [pos] # "@")
- DO
- INC (pos)
- END;
- IF pos < LEN (thisName) THEN thisName [pos] := 0X END;
- END;
- END AddUserToName;
- PROCEDURE MAIL*;
- VAR
- to : TokenPtr;
- pathName : String;
- localSym : INTEGER; (*to debug*)
- BEGIN
- Expect(SyMail);
- env.from := ""; env.to := NIL;
- Confirm(SyFrom);
- sym := SyCopy; ToMemory (env.from);
- PutStatus2 ("250 Sender okay. ", env.from);
- NEW( to );
- IF StartOf(1) THEN
- reset; IF finished THEN RETURN END;
- ELSIF (sym = SyRcpt) THEN
- RCPT;
- WHILE (sym = SyRcpt) DO
- RCPT;
- END ;
- AddUserToName (thisName);
- COPY (Prefix, pathName);
- AddExt (pathName, thisName);
- AddExt (pathName, Rcp);
- (* alm 3/16/2003 Skips previously used names. *)
- WHILE (Files.Old (pathName) # NIL)
- DO
- ConsumeName;
- AddUserToName (thisName);
- COPY (Prefix, pathName);
- AddExt (pathName, thisName);
- AddExt (pathName, Rcp);
- END;
- COPY (Prefix, pathName);
- AddExt (pathName, thisName);
- IF StartOf(1) THEN
- reset;
- ToLog0 ("Post RCPT cmd in mail.");
- KernelLog.Exit;
- IF finished THEN RETURN END;
- ELSIF (sym = SyData) THEN
- ToLog0 ("Data cmd in mail.");
- KernelLog.Exit;
- DATA (pathName);
- ELSE Error1(14)
- END ;
- ELSE Error1(15)
- END ;
- PutEnvelope (thisName);
- IF DebugMsg THEN out.Char ("@"); out.Update END;
- localSym := SELF.sym;
- PutStatus2 ("250 Your confirmation number is ", thisName);
- (* Feb. 22, 2003 *)
- CASE sym OF
- SyQuit : ToLog0 ("Quit detected.")
- | SyMail : ToLog0 ("Mail detected.")
- | SyRset : ToLog0 ("Rset detected.")
- | SyNoop : ToLog0 ("Noop detected.")
- | SyEol : ToLog0 ("dead connection detected.")
- ELSE
- ToLog0 ("Unexpected path in case statement.")
- END;
- KernelLog.Exit;
- IF sym IN {SyMail, SyRset, SyNoop} (*Noop DOES allow more mail in this session.*)
- THEN
- ToLog0 ("update name.");
- ConsumeName;
- KernelLog.Exit
- (* PutRegistry (nextName) *)
- ELSE
- ToLog0 ("Keep existing name.");
- KernelLog.Exit;
- RETURN
- END
- END MAIL;
- PROCEDURE reset;
- BEGIN
- DebugMsg1 ("Entering reset.");
- IF (sym = SyHelo) THEN HELO;
- ELSIF (sym = SyNoop) THEN NOOP;
- ELSIF (sym = SyRset) THEN RSET;
- ELSIF (sym = SyMail) THEN MAIL;
- ELSE Error1(16)
- END ;
- DebugMsg1 ("Exiting reset.")
- END reset;
- PROCEDURE Get;
- BEGIN
- INC (getCalls);
- ch := GetCh (); INC (charcount); (*No characters in buffer on entry.*)
- WHILE (ch=" ") OR (ch=Tab) DO ch := GetCh (); INC (charcount) END;
- IF ch > 7FX THEN ch := " " END;
- IF ("a"<=ch) & (ch<="z") THEN ch := CAP (ch) END;
- state := start[ORD(ch)];
- (*Intercept single character symbols to avoid read-ahead*)
- CASE state OF
- 24: sym := SyDot; RETURN
- | 3: IF (CAP(in.Peek()) ="R") THEN (* state := 35; (*does not block across CR LF on legal input.*) *)
- ELSE sym := SyCopy; RETURN
- END;
- ELSE (* Continue with multi character symbols. *)
- END;
- LOOP
- ch := GetCh (); INC (charcount);
- IF ("a"<=ch) & (ch<="z") THEN ch := CAP (ch) END;
- IF state > 0 THEN
- CASE state OF
- | 1: IF (ch=LF) THEN state := 2; sym := SyEol; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 2: HALT (52) (*Avoid look ahead character read*)
- | 3: IF (ch ="R") THEN state := 35;
- ELSE sym := SyCopy; RETURN
- END;
- | 4: IF (ch ="E") THEN state := 5;
- ELSE sym := noSym; RETURN
- END;
- | 5: IF (ch ="L") THEN state := 6;
- ELSE sym := noSym; RETURN
- END;
- | 6: IF (ch ="O") THEN state := 7; sym := SyHelo; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 7: HALT (57) (*Avoid look ahead character read*)
- | 8: IF (ch ="U") THEN state := 9;
- ELSE sym := noSym; RETURN
- END;
- | 9: IF (ch ="I") THEN state := 10;
- ELSE sym := noSym; RETURN
- END;
- | 10: IF (ch ="T") THEN state := 11; sym := SyQuit; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 11: HALT (61) (*Avoid look ahead character read*)
- | 12: IF (ch ="O") THEN state := 13;
- ELSE sym := noSym; RETURN
- END;
- | 13: IF (ch ="O") THEN state := 14;
- ELSE sym := noSym; RETURN
- END;
- | 14: IF (ch ="P") THEN state := 15; sym := SyNoop; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 15: HALT (65) (*Avoid look ahead character read*)
- | 16: IF (ch ="S") THEN state := 17;
- ELSIF (ch ="C") THEN state := 25;
- ELSE sym := noSym; RETURN
- END;
- | 17: IF (ch ="E") THEN state := 18;
- ELSE sym := noSym; RETURN
- END;
- | 18: IF (ch ="T") THEN state := 19; sym := SyRset; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 19: HALT (69) (*Avoid look ahead character read*)
- | 20: IF (ch ="A") THEN state := 21;
- ELSE sym := noSym; RETURN
- END;
- | 21: IF (ch ="T") THEN state := 22;
- ELSE sym := noSym; RETURN
- END;
- | 22: IF (ch ="A") THEN state := 23; sym := SyData; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 23: HALT (73) (*Avoid look ahead character read*)
- | 24: sym := SyDot; HALT(74); RETURN
- | 25: IF (ch ="P") THEN state := 26;
- ELSE sym := noSym; RETURN
- END;
- | 26: IF (ch ="T") THEN state := 27; sym := SyRcpt; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 27: HALT (77) (*Avoid look ahead character read*)
- | 28: IF (ch ="O") THEN state := 29;
- ELSE sym := noSym; RETURN
- END;
- | 29: IF (ch =":") THEN state := 30; sym := SyTo; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 30: HALT (80) (*Avoid look ahead character read*)
- | 31: IF (ch ="A") THEN state := 32;
- ELSE sym := noSym; RETURN
- END;
- | 32: IF (ch ="I") THEN state := 33;
- ELSE sym := noSym; RETURN
- END;
- | 33: IF (ch ="L") THEN state := 34; sym := SyMail; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 34: HALT (84) (*Avoid look ahead character read*)
- | 35: IF (ch ="O") THEN state := 36;
- ELSE sym := noSym; RETURN
- END;
- | 36: IF (ch ="M") THEN state := 37;
- ELSE sym := noSym; RETURN
- END;
- | 37: IF (ch =":") THEN state := 38; sym := SyFrom; RETURN
- ELSE sym := noSym; RETURN
- END;
- | 38: HALT (88) (*Avoid look ahead character read*)
- | 39: sym := 0; ch := 0X; RETURN
- END (*CASE*)
- ELSE sym := noSym; RETURN (*NextCh already done*)
- END; (*IF*)
- END (*LOOP*)
- END Get;
- PROCEDURE ErrMsg(msg : String);
- BEGIN
- KernelLog.String (msg);
- END ErrMsg;
- PROCEDURE Error1(n: INTEGER);
- BEGIN
- INC(errors);
- lasterror := n;
- KernelLog.Enter;
- CASE n OF
- | 13: ErrMsg("??? expected")
- | 14: ErrMsg("invalid MAIL")
- | 15: ErrMsg("invalid MAIL")
- | 16: ErrMsg("invalid reset")
- ELSE END;
- KernelLog.Exit
- END Error1;
- PROCEDURE Error2 (n, sym: INTEGER);
- BEGIN
- INC(errors);
- lasterror := n;
- KernelLog.Enter;
- CASE n OF
- 0: ErrMsg("EOF expected, ")
- | 1: ErrMsg("Eol expected, ")
- | 2: ErrMsg("ident expected, ")
- | 3: ErrMsg("'HELO' expected, ")
- | 4: ErrMsg("'QUIT' expected, ")
- | 5: ErrMsg("'NOOP' expected, ")
- | 6: ErrMsg("'RSET' expected, ")
- | 7: ErrMsg("'DATA' expected, ")
- | 8: ErrMsg("'.' expected, ")
- | 9: ErrMsg("'RCPT' expected, ")
- | 10: ErrMsg("'TO:' expected, ")
- | 11: ErrMsg("'MAIL' expected, ")
- | 12: ErrMsg("'FROM:' expected, ")
- ELSE END;
- CASE sym OF
- 0: ErrMsg("EOF found")
- | 1: ErrMsg("Eol found")
- | 2: ErrMsg("ident found")
- | 3: ErrMsg("'HELO' found")
- | 4: ErrMsg("'QUIT' found")
- | 5: ErrMsg("'NOOP' found")
- | 6: ErrMsg("'RSET' found")
- | 7: ErrMsg("'DATA' found")
- | 8: ErrMsg("'.' found")
- | 9: ErrMsg("'RCPT' found")
- | 10: ErrMsg("'TO:' found")
- | 11: ErrMsg("'MAIL' found")
- | 12: ErrMsg("'FROM:' found")
- ELSE END;
- KernelLog.Exit;
- END Error2;
- PROCEDURE Confirm(n: INTEGER);
- BEGIN IF sym = n THEN (* Nothing *) ELSE Error2(n, sym) END
- END Confirm;
- PROCEDURE Expect(n: INTEGER);
- BEGIN IF sym = n THEN Get ELSE Error2(n, sym) END
- END Expect;
- PROCEDURE StartOf(s: INTEGER): BOOLEAN;
- BEGIN RETURN (sym MOD setSize) IN symSet[s, sym DIV setSize]
- END StartOf;
- PROCEDURE Who;
- VAR ipStr : String;
- BEGIN
- IP.AdrToStr (SELF.client.fip, ipStr);
- KernelLog.String (ipStr);
- END Who;
- PROCEDURE BackStagePass (pass : String) : BOOLEAN;
- VAR ipStr : String; ix: LONGINT;
- BEGIN
- IP.AdrToStr (SELF.client.fip, ipStr);
- ix := 0;
- WHILE (ix<=15) & (ipStr[ix] = pass[ix]) & (ipStr[ix] # 0X)
- DO
- INC (ix)
- END;
- RETURN pass[ix] = 0X
- END BackStagePass;
- BEGIN {ACTIVE}
- BEGIN {EXCLUSIVE}
- INC (active)
- END;
- (* open streams *)
- Streams.OpenReader(in, client.Receive);
- Streams.OpenWriter(out, client.Send);
- IF (active < MaxActive) OR BackStagePass (AlwaysAccept)
- THEN
- ConsumeName;
- finished := FALSE;
- charcount := 0;
- getCalls := 0;
- ToLog0 ("Connection made. ");
- Who;
- KernelLog.Exit;
- Announce(out);
- ToLog0 ("Log open sequence. ");
- KernelLog.Exit;
- OpenLog;
- log.String ("Log file opened on ");
- Strings.FormatDateTime("www, dd mmm yyyy hh:nn:ss -0600 (CST)", Dates.Now(), auxString);
- log.String (auxString);
- log.Ln;
- log.String ("From IP ");
- IP.AdrToStr(SELF.client.fip, auxString);
- log.String (auxString);
- DNS.HostByNumber (SELF.client.fip, auxString, res);
- IF res = DNS.Ok
- THEN
- log.String (" <");
- log.String (auxString);
- log.String ("> ")
- END;
- log.Ln;
- ToLog0 ("Log now open. ");
- KernelLog.Exit;
- (* production Smtp *)
- Get;
- badTokens := 0;
- WHILE ~finished & (badTokens < 100) & (sym#0) DO
- WHILE ~StartOf(2) DO
- out.String ("500 Not implemented"); out.Ln; out.Update;
- ch := GetCh (); WHILE ch # CR DO ch := GetCh () END; ch := GetCh ();
- Get; INC (badTokens);
- END;
- WHILE StartOf(1)
- DO
- reset
- END;
- QUIT
- END
- ELSE
- out.String ("421 PeerGrade.mrs.umn.edu, Service Not Available, Max connections exceeded.");
- out.Ln; out.Update;
- ToLog0 ("Connection rejected, too many connections. ");
- Who;
- KernelLog.Exit
- END;
- Terminate;
- BEGIN {EXCLUSIVE} DEC (active) END;
- ToLog0 ("Connection closed. ");
- Who;
- KernelLog.Exit
- END SmtpAgent;
- VAR
- symSet: ARRAY nrSets OF SymbolSet;
- smtp: TCPServices.Service;
- nextName : String;
- PROCEDURE ToLog0 (msg : String);
- BEGIN
- KernelLog.Enter;
- KernelLog.String (ID);
- KernelLog.String (" ");
- KernelLog.String (msg);
- END ToLog0;
- PROCEDURE InitSmtpSTable;
- BEGIN
- start[0]:=39; start[1]:=0; start[2]:=0; start[3]:=0;
- start[4]:=0; start[5]:=0; start[6]:=0; start[7]:=0;
- start[8]:=0; start[9]:=0; start[10]:=0; start[11]:=0;
- start[12]:=0; start[13]:=1; start[14]:=0; start[15]:=0;
- start[16]:=0; start[17]:=0; start[18]:=0; start[19]:=0;
- start[20]:=0; start[21]:=0; start[22]:=0; start[23]:=0;
- start[24]:=0; start[25]:=0; start[26]:=0; start[27]:=0;
- start[28]:=0; start[29]:=0; start[30]:=0; start[31]:=0;
- start[32]:=0; start[33]:=0; start[34]:=0; start[35]:=0;
- start[36]:=0; start[37]:=0; start[38]:=0; start[39]:=0;
- start[40]:=0; start[41]:=0; start[42]:=0; start[43]:=0;
- start[44]:=0; start[45]:=0; start[46]:=24; start[47]:=0;
- start[48]:=0; start[49]:=0; start[50]:=0; start[51]:=0;
- start[52]:=0; start[53]:=0; start[54]:=0; start[55]:=0;
- start[56]:=0; start[57]:=0; start[58]:=0; start[59]:=0;
- start[60]:=0; start[61]:=0; start[62]:=0; start[63]:=0;
- start[64]:=0; start[65]:=0; start[66]:=3; start[67]:=0;
- start[68]:=20; start[69]:=0; start[70]:=3; start[71]:=3;
- start[72]:=4; start[73]:=0; start[74]:=3; start[75]:=3;
- start[76]:=0; start[77]:=31; start[78]:=12; start[79]:=0;
- start[80]:=0; start[81]:=8; start[82]:=16; start[83]:=0;
- start[84]:=28; start[85]:=0; start[86]:=3; start[87]:=3;
- start[88]:=3; start[89]:=3; start[90]:=3; start[91]:=0;
- start[92]:=0; start[93]:=0; start[94]:=0; start[95]:=0;
- start[96]:=0; start[97]:=0; start[98]:=3; start[99]:=0;
- start[100]:=0; start[101]:=0; start[102]:=3; start[103]:=3;
- start[104]:=0; start[105]:=0; start[106]:=3; start[107]:=3;
- start[108]:=0; start[109]:=0; start[110]:=0; start[111]:=0;
- start[112]:=0; start[113]:=0; start[114]:=0; start[115]:=0;
- start[116]:=0; start[117]:=0; start[118]:=3; start[119]:=3;
- start[120]:=3; start[121]:=3; start[122]:=3; start[123]:=0;
- start[124]:=0; start[125]:=0; start[126]:=0; start[127]:=0;
- END InitSmtpSTable;
- PROCEDURE NewSmtpAgent(c: TCP.Connection; s: TCPServices.Service): TCPServices.Agent;
- VAR a: SmtpAgent;
- BEGIN
- NEW(a, c, s); RETURN a
- END NewSmtpAgent;
- (* This should become XML aware. *)
- PROCEDURE GetRegistry (VAR filename : String);
- VAR regF : Files.File; regR : Files.Reader;
- BEGIN
- regF := Files.Old (ConfigFileName);
- IF regF # NIL
- THEN
- Files.OpenReader (regR, regF, 0);
- regR.RawString (filename)
- ELSE
- filename := "D0000000000.Msg";
- regF := Files.New (ConfigFileName);
- Files.Register (regF)
- END;
- END GetRegistry;
- PROCEDURE PutRegistry (VAR filename : String);
- VAR regF : Files.File; regW : Files.Writer;
- BEGIN
- regF := Files.Old (ConfigFileName);
- IF regF=NIL THEN regF := Files.New (ConfigFileName); Files.Register (regF) END;
- Files.OpenWriter (regW, regF, 0);
- regW.RawString (filename);
- regW.Update;
- regF.Update;
- END PutRegistry;
- PROCEDURE Announce ( VAR out: Streams.Writer);
- BEGIN
- out.String ("220 ");
- out.String (DNS.domain);
- out.Char (" ");
- out.String ("SMTP");
- out.Char (" ");
- out.String (ID);
- out.String (Version);
- out.String (" Ready ");
- out.Ln();
- out.Update;
- END Announce;
- PROCEDURE Open*;
- VAR res : WORD;
- BEGIN
- IF smtp = NIL THEN
- NEW(smtp, AlmSmtpReceiverPort, NewSmtpAgent, res);
- active := 0;
- GetRegistry (nextName);
- ToLog0 (Version); KernelLog.String(" opened. Next name: ");
- KernelLog.String (nextName);
- KernelLog.Exit
- END;
- END Open;
- PROCEDURE Close*;
- BEGIN
- IF smtp # NIL THEN
- smtp.Stop(); smtp := NIL;
- PutRegistry (nextName);
- ToLog0 (Version); KernelLog.String(" closed"); KernelLog.Exit
- END;
- END Close;
- PROCEDURE Cleanup;
- BEGIN
- Close;
- END Cleanup;
- BEGIN
- Pattern[0] := CR;
- Pattern[1] := LF;
- Pattern[2] := ".";
- Pattern[3] := CR;
- Pattern[4] := LF;
- Pattern[5] := 0X;
- Ack[0] := "0";
- Ack[1] := "1";
- Ack[2] := "2";
- Ack[3] := "3";
- Ack[4] := "4";
- Ack[5] := 0X;
- symSet[0, 0] := {0};
- symSet[1, 0] := {SyHelo,SyNoop,SyRset,SyMail};
- symSet[2, 0] := {SyHelo,SyQuit,SyNoop,SyRset,SyMail};
- InitSmtpSTable;
- Modules.InstallTermHandler(Cleanup);
- END AlmSmtpReceiver.
- AlmSmtpReceiver.Tool
- System.Directory FAT:/Mail/Incoming/*\d
- System.Directory C0*\d
- Aos.Call AlmSmtpReceiver.Open
- Aos.Call AlmSmtpReceiver.Close
- Aos.Call NetTracker.Open 100 ~
- System.Free AlmSmtpReceiver ~
- System.Free AlmSmtpReceiver ~
- EditTools.OpenAscii ^
- Telnet.Open cda
- System.State AlmSmtpReceiver ~
- Builder.Compile *
- Telnet.Open "sci1355-am.mrs.umn.edu" 27
- Colors.Panel
- Hex.Open mail.config
- ch = 0000000DX
- charcount = 26
- config = ""
- errors = 0
- lasterror = 0
- nextName = "D0000000101"
- smtp = 022685D0H
- start = 39, 0, 0, 0, 0, 0, 0, 0, 0, 0 ...
- state = 7
- sym = 3
|