Trailing-Edge
-
PDP-10 Archives
-
msv11ck
-
10,7/mail/mx/smtlis.mac
There are 21 other files named smtlis.mac in the archive. Click here to see a list.
TITLE LISTEN - SMTP Listener
TWOSEG
RELOC 400000
;Cloned by MX. Does not return or get killed.
SEARCH MONSYM,MACSYM
EXTERN DB%VD8,MX%UNQ,MX$VAL,NODNAM,TBL%LO,UF%CLO,UN%ACC
EXTERN UF%OPE,UF%WRI,UM%GET,UM%REL,UN%CLO,UN%REA,UN%WRI,SCAN%P
EXTERN UN%OPEN,MX$TIM,RELASC,UT%TEX,MXERRS,NMLDIE,LOG
; EXTERN BROAD ;-20 ONLY
INTERN SMTPLI,STRLEN,STRLN
IP$JAC=400000000000 ;Like SC%WHL for TOPS10
FTLOG==0 ;NO LOGGING NOW (20 ONLY)
;Offsets into various MX data structures
.DESTN==3
.FILSP==1
.POST==1
.DONE=2
.SENDR==2
.DECNT==1
.RECNM==0
.RECTY==1
.RECLE==2
.RECTX==3
.PKTYP==0
.PKID==1
.PKSEQ==2
.PKSTS==3
.PKRCT==4
;Ac definitions
F==0
T1==1
T2==2
T3==3
T4==4
T5==5
T6==6
RCPTPT==7 ;LINKED LIST OF RECEIPIENTS
CONBLK==10 ;ADDR OF CONNECT BLOCK
REQLEN==11 ;LENGTH OF REQUEST BLOCK
REQ==12 ;ADDRESS OF REQUEST BLOCK
;DON'T USE 13, RESERVED FOR BLISS
DECNET==14 ;DECNET CHANNEL NUMBER
;15 IS RESERVED FOR BLISS
VR==16 ;POINTER TO VARIABLE SPACE
P==17
;Object type
OBJTYP==^D0 ;Using name not number
;OFFSETS INTO BLOCK OF INFORMATION AT VR (Local data)
TXTPTR==0 ;POINTER TO LAST LINE READ BUFFER
TXTLEN==1 ;LENGTH OF LAST READ IN TEXT
FRMPTR==2 ;POINTER TO "FROM" BUFFER
RSPPTR==3 ;POINTER TO SPACE FOR RESPONSES
SCRWRD==4 ;SCRATCH FOR ROUTINE RETURNS
CMDWRD==5 ;FIRST 5 CHARS OF INCOMING COMMAND
TMPPTR==6 ;POINTER TO CHAIN OF RECEIPIENTS
ATPNTR==7 ;POINTER TO THE @ IN A "TO" STRING
ENPNTR==10 ;POINTER TO END OF NODENAME
USPNTR==11 ;POINTER TO BEGINNING OF USERNAME
NXTPTR==12 ;POINTER TO NEXT SUBBLOCK TO CREATE
TXTBLK==13 ;TEXT ARG BLOCK FOR UT%TEX
FILE==14 ;FILE NUMBER
OUTBFR==15 ;pointer to output buffer begin
OUTPNT==16 ;pointer into output buffer
OUTCNT==17 ;number of chars left in buffer
SENDFL==20 ;faking SEND, not doing MAIL
STARTP==21 ;Stack pointer at startup
SAVACL==22 ;ac save block
N%VAR=SAVACL+4+1 ;NUMBER OF VARIABLES NEEDED
NXT==0 ;NEXT POINTER FOR RCPT LINKED LIST
;LH IS LENGTH OF BLOCK, RH IS NEXT BLOCK ADDR (OR 0)
;Constants of merit
CONLEN==^D64 ;SIZE OF DECNET CONNECT BLOCK
OUTLEN==1000 ;NUMBER OF WORDS FOR FILE OUTPUT BUFFER
MAXREC==1000*^D8*5 ;MAX NUMBER OF CHARACTERS WE CAN RECEIVE
MAXSND==^D1000 ;MAX WE WILL SEND, OR EXPECT FOR NON-MESSAGES
TXBLEN==10 ;FOR UT%TXT
;These allow us to format up SMTP responses
DEFINE $RSPC(val),<
MOVEI T1,^D val
DMOVEM T1,@TXTBLK(VR)
MOVE T1,[POINT 7,STDSTR]
MOVEI T2,2 >
DEFINE $RSPA(ind),<
EXTERN ind
MOVE T2,MXERRS+ind >
DEFINE $RSE(val,ind),<
$RSPA (ind)
$RSPC (val) >
DEFINE $RSP(val,text),<
MOVE T2,[POINT 7,[ASCIZ `text`]]
$RSPC(val) >
define logger(txt),<
IFN FTLOG,<
CALL [PUSH P,[ -1,,[ASCIZ/
********** txt
/]]
call logers
adjsp p,-1
RET]
> >
define logget(addr),<
IFN FTLOG,<
CALL [PUSH P,addr
call logers
adjsp p,-1
RET]
> >
IFN FTLOG,<
logers: PUSH P,T1
PUSH P,T2
MOVX T1,GJ%SHT+GJ%OLD
HRROI T2,[ASCIZ/MX:SMTLIS.LOG/]
GTJFN%
ERJMP NOLOGR
MOVX T2,OF%APP+7B5
OPENF%
ERJMPS NOLOGX
PUSH P,T3
SETZ T3,
move t2,-4(p)
sout%
erjmps .+1
pop p,T3
nologx: closf%
erjmps .+1
nologr: pop p,t2
pop p,t1
ret
>
DEFINE GETMEM(size),<
%$$==<size>
PUSH P,[%$$]
CALL SVACLI
CALL UM%GET
CALL RSACLI
ADJSP P,-1
JUMPE T1,NOMEME
>
DEFINE SETADR(whrpnt,size),<
MOVEM T1,whrpnt
ADDI T1,<size>
%$$==%$$-<size>
IFLE %$$,<PRINTX Not enough memory asked for in SMTLIS>
>
ALLMEM=< N%VAR+CONLEN+TXBLEN+OUTLEN+<MAXREC/5+1>+3*<MAXSND/5+1> >+1
;Start here
SMTPLI: GETMEM (ALLMEM)
SETADR VR,N%VAR ;N%VAR WORDS POINTED TO BY VR
SETADR CONBLK,CONLEN ;CONLEN WORDS POINTED TO BY CONBLK
SETADR TXTBLK(VR), TXBLEN ;TXBLEN WORDS POINTED TO BY TXTBLK(VR)
HRLI T1,(POINT 7) ;EVERYTHING BELOW IS A BP
SETADR OUTBFR(VR), OUTLEN ;OUTPUT BUFFER
SETADR TXTPTR(VR), MAXREC/5+1 ;INPUT FROM DECNET
SETADR FRMPTR(VR), MAXSND/5+1 ;FOR PROCESSING FROM, ETC.
SETADR RSPPTR(VR), MAXSND/5+1 ;FPR BUILDING RESPONSES
SETADR TMPPTR(VR), MAXSND/5+1 ;STRING HACK SPACE
SETZB RCPTPT,FILE(VR) ;NO FILE YET, NO RECPIENTS
MOVEM P,STARTP(VR) ;SAVE STARTING STACK POINTER
;Come back here to set up the next connection
GOAGAI: MOVE P,STARTP(VR) ;START WITH RIGHT STACK
SETZM 0(CONBLK)
MOVSI T1,(CONBLK) ;SET UP TO CLEAR THE CONNECT BLOCK
HRRI T1,1(CONBLK)
BLT T1,CONLEN-1(CONBLK)
; MOVX T1,OBJTYP ;STORE OBJECT TYPE
; MOVEM T1,2(CONBLK) ;..DON'T, IS A 0
;
;SET UP A SOURCE TASK NAME IN CB_TASK
;
MOVE T1,[POINT 7,[ASCII /MX-SENDER/]] ;Who we want to hear from
MOVEM T1,3(CONBLK)
MOVEI T1,^D9 ;LENGTH OF ABOVE TASK NAME
MOVEM T1,4(CONBLK)
;
;SET UP DESTINATION TASK NAME IN CB_DESCRIPTOR FIELD
;
MOVE T1,[POINT 7,[ASCIZ /MX-LISTENER/]] ;Who we are
MOVEM T1,15(CONBLK)
MOVEI T1,^D11 ;LENGTH OF ABOVE TASK NAME
MOVEM T1,16(CONBLK)
PUSH P,[1] ;CONNECT TYPE 1, DESTINATION
PUSH P,CONBLK
PUSH P,[0]
PUSH P,[0]
PUSH P,[0]
logger <Attempting to open a SMTP Receive connection>
CALL SVACLI
CALL UN%OPEN ;Open the recieving link
CALL RSACLI
ADJSP P,-5
SKIPG DECNET,T1 ;OK? STORE RESULTING LINK-ID
JRST DIENOW ;FAILED
logger <Opened, sending accept>
PUSH P,DECNET ;SET UP TO ACCEPT LINK
PUSH P,[0]
PUSH P,[0]
CALL SVACLI
CALL UN%ACCEPT
CALL RSACLI
ADJSP P,-3
;SAY HELLO
SETZ T4, ;BUILD ONLINE MESSAGE
MOVE T2,RSPPTR(VR)
HRROI T1,[ASCIZ/220 /] ;SMTP PROTOCOL VALUE FOR ONLINE MESSAGE
CALL CSTRB
MOVE T1,[POINT 7,NODNAM] ;ADD OUR NODENAME
CALL CSTRB
HRROI T1,[ASCIZ/ Ready
/] ;+<SP>message
CALL CSTRB
CALL DNTOUT ;WRITE MESSAGE OUT
;Get greeting
GETGRT: logger <Waiting for hello message>
CALL DCTREA ;WAIT FOR HELLO MESSAGE
MOVEI T1,TABLE1 ;LOOK ON STATE TABLE 1 FOR REACTION
CALL CMDFND ;..
JRST NOHELO ;COMMAND NOT KNOWN
JRST (T1) ;KNOWN, DISPATCH
TABLE1: TLEN1,,TLEN1
[ASCIZ/HELO/],,GOTHLO ;HELO, ALL SET, ENTER STATE 2
[ASCIZ/NOOP/],,OKGGRT ;NOOP, FINE, IGNORE
[ASCIZ/QUIT/],,QUIT ;QUIT, GO START HANGING UP
[ASCIZ/RSET/],,OKGGRT ;RSET, FINE, IGNORE
TLEN1=.-TABLE1-1
;Here to set up for next state 2 command
GOTHLO: CALL READY ;CLEAR OUT EVERYTHING
OKCMD: $RSP (250,OK) ;SAY LAST COMMAND WAS OK
logger <Sending OK>
RSPCMD: CALL SNDRSP ;SEND MESSAGE IN T1
NXTCMD: CALL DCTREA ;GET A COMMAND
;TO LOSE TIMING CHECK, COMMENT OUT NEXT 5 LINES
CALL SVACLI
CALL MX$TIM ;GET # OF SECS TILL SYSTEM GOES DOWN
CALL RSACLI
CAIGE T1,^D60 ;GOT 60 MORE SECONDS?
JRST SYSDWN ;NO - CLOSE UP
;END TIMING CHECK
MOVEI T1,TABLE2 ;LOOK ON TABLE FOR STATE 2 COMMANDS
CALL CMDFND
JRST NOCMD ;NOT KNOWN
JRST (T1) ;DISPATCH
;What we expect to get once the premilinaries are out of the way
TABLE2: TLEN2,,TLEN2
[ASCIZ/DATA/],,DODATA ;DATA, RECEIVE TEXT
[ASCIZ/HELO/],,GOTHLO ;HELO, TREAT LIKE RSET
[ASCIZ/MAIL/],,DOFROM ;MAIL, START OF A MAIL TRANSCATION
[ASCIZ/NOOP/],,OKCMD ;NOOP, OUR FAVORITE
[ASCIZ/QUIT/],,QUIT ;QUIT, GO END CONVERSATION
[ASCIZ/RCPT/],,DOTO ;RCPT, ACCEPT (ANOTHER) RECEIPIENT
[ASCIZ/RSET/],,GOTHLO ;RSET, CLEAR OUT ALL BUFFERS
[ASCIZ/SEZZ/],,DOSEND ;SEND, DO A SPECIALIZED SEND
[ASCIZ/SOML/],,DOFROM ;SOML, TREAT LIKE MAIL
[ASCIZ/VRFY/],,VERIFY ;VRFY, VERIFY A USER
TLEN2=.-TABLE2-1
NOCMD: CALL WHAT ;Here if we get something we didn't
JRST NXTCMD ;Expect. Send back the right 50n.
DOSEND: SETOM SENDFL(VR)
logger <Got SEZZ command>
JRST DOXXXX
;FROM command
DOFROM: SETZM SENDFL(VR)
logger <Got FROM command>
DOXXXX: MOVE T1,TXTPTR(VR)
ADDI T1,1 ;SKIP FIRST 5 CHARS
FNDFRS: ILDB T3,T1 ;SCAN FOR ":"
JUMPE T3,BADCMD ;NEVER SAW IT, BAD
CAIE T3,":"
JRST FNDFRS ;SCAN SMORE
MOVE T2,FRMPTR(VR) ;GOT IT, COPY REST TO FRMPTR(VR)
FROMCP: ILDB T3,T1 ;COPY REV. PATH WITHOUT <CRLF>
CAIE T3,.CHCRT ;STOP ON CR..
CAIN T3,.CHLFD ;OR LF
SETZ T3,
IDPB T3,T2
JUMPN T3,FROMCP ;KEEP GOING UNTIL ALL READ
$RSP (250,OK for mail) ;RESPOND OK
JRST RSPCMD
;Here if we get trash
BADCMD: $RSE (501,MG$ARG)
JRST RSPCMD
;These routines attempt to verify a username. They measure the provided
; string and pass it to MX$VAL. The return value is MX$VAL's.
VERIUS: MOVE T1,TXTPTR(VR)
ADDI T1,1
VERIST: MOVE T5,T1
SETZ T4,
VERCHP: ILDB T3,T1
JUMPE T3,NOCRVY
CAIE T3,.CHCRT
AOJA T4,VERCHP
NOCRVY: JUMPE T4,BADCMD
PUSH P,T4
PUSH P,T5
HRRZ T1,TMPPTR(VR)
PUSH P,T1
CALL SVACLI
CALL MX$VAL
CALL RSACLI
ADJSP P,-3
RET
;The VRFY command comes here. Verify and say "250 <name>" or "Not here"
VERIFY: CALL VERIUS
TRNE T1,1
JRST VEROKR ;USER IS OK
$RSP (550,User not known here)
JRST RSPCMD
VEROKR: MOVE T2,TXTPTR(VR) ;MOVE NAME INTO <NAME@USNODE>
MOVE T1,T2 ;MAY AS WELL USE SAME STRING SPACE
ADDI T1,1 ;USERNAME STARTS ONE WORD DOWN
MOVEI T3,"<" ;MOVE WEDGY IN
IDPB T3,T2
VEROK2: ILDB T3,T1 ;COPY USERNAME UNTIL <CR> OR NULL
JUMPE T3,VEROK3
CAIN T3,.CHCRT
JRST VEROK3 ;OK, ADD @NODENAME
IDPB T3,T2
JRST VEROK2
VEROK3: MOVEI T3,"@"
IDPB T3,T2 ;@
MOVE T1,[POINT 7,NODNAM]
VEROK4: ILDB T3,T1 ;COPY NODENAME
JUMPE T3,VEROK5
IDPB T3,T2
JRST VEROK4
VEROK5: MOVEI T3,">"
IDPB T3,T2
SETZ T3,
IDPB T3,T2 ;NULL TERMINATE
MOVE T2,TXTPTR(VR)
$RSPC (250) ;SAY OK, AND GIVE NAME BACK
JRST RSPCMD
;MAIL command
;This is messy
;SMTP allows strings in the form <user@node> or <@node,@node,@node..:user@node>
; user@node is easy, just verify node, and if it is us, verify user
; @node...:user@node is messy. Toss the first node (it should be us), verify
; the second one in the list, and pass the rest verbatim.
DOTO: logger <Got MAIL command>
MOVE T1,TXTPTR(VR)
ADDI T1,1 ;SKIP FIRST 5 CHARS ("MAIL ")
MOVE T4,TXTLEN(VR)
SUBI T4,5 ;ACCOUNT FOR 5 CHARS SKIPPED
FNDTOS: ILDB T3,T1 ;FETCH
JUMPE T3,BADCMD ;NULL? BAD COMMAND
CAIE T3,"<" ;FIND THE OPEN ANGLE BRACKET
SOJA T4,FNDTOS ;NOPE, ANOTHER CHAR GONE
MOVE T6,T1 ;GOT IT, SAVE POINTER
IDIVI T4,5 ;HOW MUCH SPACE TO HOLD THIS STRING?
MOVEI T4,2(T4) ;LEAVE SOME EXTRA
PUSH P,T4 ;FETCH THE MEMORY
CALL SVACLI
CALL UM%GET ;GET ENOUGH MEMORY TO HOLD RCPT STRING & HEADER
CALL RSACLI
ADJSP P,-1
JUMPE T1,STORFL ;NO ROOM? ABORT!
logger <MAIL command has enough memory>
HRL RCPTPT,T4 ;MAKE LEN,,POINTER TO NEXT BLOCK
MOVEM RCPTPT,NXT(T1) ;STORE IN NEW BLOCK
MOVE RCPTPT,T1 ;LINK IN
ADD T1,[POINT 7,1] ;POINT TO STRING DESTINATION
SETZM ATPNTR(VR) ;NO "@" YET
SETZM USPNTR(VR) ;NO USER NAME YET
SETZB T4,ENPNTR(VR) ;NO NODENAME YET, NO FLAGS
CPTOST: ILDB T3,T6 ;SCAN STRING, PARSING OUT NODENAME, ETC.
CAIN T3,">" ;TREAT CLOSE ANGLE AS TERMINATOR
SETZ T3, ;..
JUMPE T3,CHKTOS ;END OF STRING
CAIN T3,"\" ;QUOTING CHAR?
JRST LITTOA ;YES, GO HANDLE
CAIE T3,"@" ;NODENAME STARTS HERE?
JRST LITTOS ;NO, DOING USERNAME
TRNN T4,2 ;YES, GOT A USERNAME YET?
TROE T4,1 ;NO, IS THIS FIRST NODENAME?
JRST CPFRND ;NOT A FORWARDING REQUEST.
;HERE WE SEE @US,@OTHERSTUFF. REMOVE "@US,"
TOSSLF: ILDB T3,T6 ;GOBBLE FIRST NODENAME (IT'S US)
JUMPE T3,NOUSER ;ERROR - NO USER OR NODENAME
CAIE T3,":" ;LOOK FOR NODENAME TERMINATOR
CAIN T3,"," ;..
JRST CPTOST ;SKIPPED OUR NODENAME, BACK TO SCANNING
JRST TOSSLF ;KEEP SCANNING
;HERE IF WE HAVE "USER@NODENAME" REMEMBER WHERE THE "@" IS
CPFRND: IDPB T3,T1 ;PUT "@" IN
MOVEM T1,ATPNTR(VR) ;AND STORE POINTER TO IT
;HERE TO COPY OUT A NODENAME
CPTOND: ILDB T3,T6
CAIN T3,">"
SETZ T3,0
IDPB T3,T1 ;COPY CHARACTER
JUMPE T3,ENDNDS ;END, GO SEE WHAT WE GOT
CAIE T3,":"
CAIN T3,"," ;NODENAME END?
JRST ENDNDS ;YES, GO SEE WHAT WE GOT
JRST CPTOND ;KEEP COPYING NODENAME
ENDNDS: MOVEM T1,ENPNTR(VR) ;STORE POINTER TO END OF NODE
JUMPE T3,CHKTOS ;WAS LAST CHAR NULL?
CPBLIN: ILDB T3,T6 ;NO, COPY REST OF STRING VERBATIM
IDPB T3,T1
JUMPN T3,CPBLIN
JRST CHKTOS ;AND GO DEAL WITH WHAT'S REVELANT TO US
LITTOA: ILDB T3,T6 ;HERE FOR QUOTED CHAR, FETCH IT
LITTOS: TRON T4,2 ;HAVE WE STARTED USERNAME ALREADY?
MOVEM T1,USPNTR(VR) ;NO, STORE POINTER TO IT
IDPB T3,T1 ;WRITE USERNAME CHARACTER
JRST CPTOST ;GO AGAIN
;DONE PARSING, SEE WHAT WE GOT
CHKTOS: SKIPN T1,ATPNTR(VR) ;WAS A NODENAME SEEN?
JRST LCLMAL ;NO, JUST A LOCAL USERNAME
LDB T3,ENPNTR(VR) ;FETCH CHARACTER THAT ENDS STRING AND SAVE IT
SETZ T2, ;PREPARE TO NULL IT
DPB T2,ENPNTR(VR) ;..
PUSH P,T1 ;VALIDE THE NODENAME
PUSH P,[1] ;PUSH LOCAL DOMAIN
PUSH P,[-1] ;PUSH UNKNOWN DOMAIN
CALL SVACLI
CALL DB%VD8 ;VALID8 NODE
CALL RSACLI
ADJSP P,-3 ;FIX STACK
DPB T3,ENPNTR(VR) ;REPAIR STRING
JUMPL T1,BADNOD ;UNKNOWN NODE, WE CAN'T DO THIS
JUMPN T3,USRNTL ; "," OR ":" FORCE FORWARDING (USER NOT LOCAL)
JUMPG T1,USRNTL ;.GT. 0 IF NOT LOCAL, HANDLE AS SUCH
;THE NODE IS LOCAL, LET'S DO THE USERNAME
LCLMAL: SKIPN T1,USPNTR(VR) ;POINTER TO USERNAME TO CHECK
JRST NOUSER ;NONE GIVEN, COMPLAIN
SETZ T2, ;MAKE USERNAME NULL TERMINATED
DPB T2,ATPNTR(VR) ;NULL OUT "@"
CALL VERIST ;VERIFY USER
MOVEI T2,"@"
DPB T2,ATPNTR(VR) ;REPAIR STRING
TRNN T1,1 ;REAL USERNAME?
JRST NOUSER ;NO, COMPLAIN
USRNTL: logger <username acceptable>
JRST OKCMD ;OK STRING IF NOT LOCAL OR GOOD LOCAL USERNAME
NOUSER: logger <username unacceptable (not local)>
MOVE T1,USPNTR(VR) ;NEVER HEARD OF THE BLIGHTER
$RSPA (SL$NSU)
JRST ERR550
BADNOD: logger <nodename not known>
MOVE T1,ATPNTR(VR) ;NEVER HEARD OF THIS NODE
$RSPA (SL$NNK)
ERR550: MOVE T3,TXTBLK(VR) ;ERROR 550, BAD RECIPIENT
MOVEM T1,2(T3)
MOVEI T1,^D550
DMOVEM T1,(T3)
MOVEI T2,3
HRROI T1,STDSTI
PUSH P,T1
PUSH P,T2
HLRZ T4,NXT(RCPTPT) ;DELETE THIS RECIPIENT
PUSH P,RCPTPT
PUSH P,T4
HRRZ RCPTPT,NXT(RCPTPT)
CALL SVACLI
CALL UM%REL
CALL RSACLI
ADJSP P,-2
POP P,T2
POP P,T1
JRST RSPCMD
;HERE TO CLEAR OUT OLD COMMAND STATES. TOSS OLD RCPT LINKED LIST
; ZERO APPROPRATE VARIABLES, DROP OPEN FILES, ETC.
READY: logger <At READY (clearing out old data)>
JUMPE REQ,NOREQK
PUSH P,REQ
PUSH P,REQLEN
CALL UM%REL
ADJSP P,-2
SETZ REQ, ;NO REQUEST BLOCK
NOREQK: SKIPN T1,FILE(VR) ;ANY FILE OPEN?
JRST NOFILE
PUSH P,T1 ;YES, ABORT IT
PUSH P,[1] ;ABORT FLAG
PUSH P,[0] ;DON'T WANT ERROR STRING
CALL SVACLI
CALL UF%CLO
CALL RSACLI
ADJSP P,-3
SETZM FILE(VR) ;GONE
NOFILE: JUMPE RCPTPT,ENDTOL ;ANY RECPIENTS LEFT?
NXTTOK: HLRZ T4,NXT(RCPTPT) ;YES, FETCH LENGTH OF THIS BLOCK
PUSH P,RCPTPT ;ARG: ADDRESS
PUSH P,T4 ;ARG: LENGTH
HRRZ RCPTPT,NXT(RCPTPT) ;ADVANCE FOR NEXT BLOCK (OR 0)
CALL SVACLI
CALL UM%REL ;DELETE BLOCK
CALL RSACLI
ADJSP P,-2
JUMPN RCPTPT,NXTTOK ;DO NEXT IF NOT DONE
ENDTOL: HRRZ T1,FRMPTR(VR) ;CLEAR OUT FROM BUFFER
SETZM (T1) ;..
RET ;READY TO START AFRESH
;DATA COMMAND
DODATA: JUMPN RCPTPT,DATAST ;ANY RECIPIENTS ON THE LIST?
$RSP (554,No legal receivers given)
JRST DATANO ;NO, NO GOOD RECIPIENT RECIEVED
DATAST: MOVE T1,FRMPTR(VR) ;POINT TO FROM STRING
logger <Got DATA with good previous receivers>
SKIPE (T1) ;ANY "FROM"?
JRST DATAS1 ;OK, GOOD
$RSP (554,No MAIL command given)
JRST DATANO
SKIPE SENDFL(VR)
JRST [$RSP (357,Go wild)
CALL SNDRSP ;SEND REQUEST, THIS IS SPECIAL
JRST SENDI2]
;Here to start creating a buffer of information to hand to MX
DATAS1: CALL STRLN ;COUNT CHARS REF'D BY T1, TO T4
IDIVI T4,5 ;HOW MANY WORDS?
MOVEI REQLEN,4(T4) ;ANSWER INTO REQLEN, PLUS HEADER
MOVE T2,RCPTPT ;NOW ADD SPACE FOR EACH RECPIENT STRING
CNTUP: HLRZ T1,NXT(T2) ;GET LENGTH..
ADDI REQLEN,4(T1) ;ADDED IN, PLUS HEADER
HRRZ T2,NXT(T2) ;GET NEXT BLOCK
JUMPN T2,CNTUP ;IF THERE IS ONE
ADDI REQLEN,^D20 ;ADD SPACE FOR FILE NAME AND HEADERS AND EXTRA
PUSH P,REQLEN ;REQUEST THAT MUCH
CALL SVACLI
CALL UM%GET ;..
CALL RSACLI
ADJSP P,-1 ;..
SKIPN REQ,T1 ;STORE ADDRESS OF BLOCK IN REQ
JRST STORFL ;NO MEMORY
;SET UP 5 WORD BLOCK HEADER
HRRI T1,.POST ;First block
HRLI T1,.DECNT ;We're a decnet listener
MOVEM T1,.PKTYP(REQ) ;NEW COMMAND BUFFER
SETZM .PKID(REQ) ;NO ID NUMBER
SETZM .PKRCT(REQ) ;NO RECORDS SO FAR
MOVEI T1,1
MOVEM T1,.PKSEQ(REQ) ;FIRST IN GROUP (OF 1)
MOVEM T1,.PKSTS(REQ) ;..
;SET UP FIRST RECORD, FILENAME
MOVEI T5,.PKRCT+1(REQ) ;POINT TO FIRST BLOCK
AOS T1,.PKRCT(REQ) ;HAVE A NEW RECORD IN THE BLOCK
MOVEM T1,.RECNM(T5) ;ADD RECORD NUMBER IN
MOVEI T1,.FILSP ;TYPE IS FILESPEC
MOVEM T1,.RECTY(T5) ;..
CALL SVACLI
CALL MX%UNQ ;GENERATE A FILENAME THAT'S UNIQUE
CALL RSACLI
HRLI T1,(POINT 7) ;RETURNS ADDRESS OF STRING
MOVEI T2,(T5) ;POINT TO SUBBLOCK START
ADD T2,[POINT 7,.RECTX] ;BUILD BYTE POINTER TO FILENAME SECTION
PUSH P,T2 ;NOTE! FOR CALL TO UF%OPE BELOW!
PUSH P,T1 ;SAVE FOR RELASC
SETZ T4, ;NEED LENGTH OF THIS STRING
CALL CSTRB
HRRZS (P) ;REDUCE BP TO ADDRESS
CALL SVACLI
CALL RELASC ;AND RELEASE STRING CREATED BY MX%UNQ
CALL RSACLI
ADJSP P,-1 ;TOSS SOURCE ADDR OFF STACK
MOVE T1,T4 ;GET STRING LENGTH INTO T1
IDIVI T1,5 ;GET STRING LENGTH IN WORDS
ADDI T1,3+1 ;CORRECT LENGTH, AND ADD IN HEADER LENGTH
MOVEM T1,.RECLE(T5) ;AND STORE
ADDI T5,(T1) ;UPDATE T5 TO POINT TO NEXT BLOCK TO CREATE
MOVEM T5,NXTPTR(VR) ;AND STORE
PUSH P,[2] ;WRITE ACCESS (ADDRESS OF NAME ALREADY STACKED)
PUSH P,[0] ;NO ERROR BUFFER
CALL SVACLI
CALL UF%OPE ;OPEN FILE FOR WRITE
CALL RSACLI
ADJSP P,-3 ;REBALANCE STACK
JUMPLE T1,STORFA ;CHECK IF OK, COMPLAIN IF NOT
FILEOP: MOVEM T1,FILE(VR) ;RECORD FILE NUMBER
$RSP (354,Go)
CALL SNDRSP ;SAY "OK FOR TEXT"
logger <GO given for DATA>
;BUILD DATA STRUCTURE WHILE WAITING FOR INCOMING MAIL
MOVE T5,NXTPTR(VR)
AOS T1,.PKRCT(REQ)
MOVEM T1,.RECNM(T5)
MOVEI T1,.SENDR
MOVEM T1,.RECTY(T5)
MOVE T1,FRMPTR(VR) ;COPY REVERSE PATH IN
MOVE T2,T5 ;BUILD POINTER INTO REQ BLOCK
ADD T2,[POINT 7,.RECTX] ;..
SETZ T4,
CALL CSTR ;COPY STRING AND GET LENGTH
IDIVI T4,5
ADDI T4,3+1 ;SIZE OF BLOCK PLUS HEADER
MOVE T5,NXTPTR(VR)
MOVEM T4,.RECLE(T5)
ADDI T5,(T4) ;GET ADDR NEXT BLOCK IN T5
RECPCP: AOS T1,.PKRCT(REQ)
MOVEM T1,.RECNM(T5)
MOVEI T1,.DESTN
MOVEM T1,.RECTY(T5)
MOVE T2,T5 ;BUILD POINTER TO RECEIPIENT NAME
ADD T2,[POINT 7,.RECTX] ;..
MOVE T1,RCPTPT ;GEN POINTER TO WHERE IT IS NOW
ADD T1,[POINT 7,1] ;SKIP HEADER..
SETZ T4,
CALL CSTR ;COPY IT
MOVE T1,T4
IDIVI T1,5 ;HOW LONG?
ADDI T1,3+1 ;PLUS HEADER PLUS NULL
MOVEM T1,.RECLE(T5)
ADDI T5,(T1) ;POINT TO NEXT BLOCK
PUSH P,RCPTPT ;TOSS USED RECIPIENT BUFFER
HLRZ T1,NXT(RCPTPT) ;..
PUSH P,T1 ;..
HRRZ RCPTPT,NXT(RCPTPT) ;ADVANCE POINTER FOR NEXT TIME
CALL SVACLI
CALL UM%REL ;ZAP
CALL RSACLI
ADJSP P,-2
JUMPN RCPTPT,RECPCP ;IS THERE MORE?
;SET UP TO READ/PARSE THE MESSAGE AND WRITE IT TO A FILE
;TERMINATE ON A LINE BEGINNING WITH ".<CR><LF>"
;IN HERE, T6 IS -1 IF WE JUST READ A <LF> (OR ARE JUST STARTING),
; AND IS 1 IF WE JUST READ A <LF> FOLLOWED BY "." (NEXT WILL BE "." OR <LF>)
; ANY OTHER TIME WE KEEP IT 0
;TXTLEN(VR) IN THE NUMBER OF CHARACTERS LEFT IN THE READ BUFFER (SET BY DCTREA)
;OUTCNT(VR) IS THE NUMBER OF CHARACTERS WRITTEN INTO OUTBFR
SENDI2: SETO T6, ;FLAG: DOT IS MEANINGFUL
SETZM TXTLEN(VR) ;EMPTY BUFFER
logger <Initing up for DATA receives>
JRST SETOUT ;GO INITIALIZE
;Here, read a line, check for leading ".", and store text or finish
INLOOP: CALL DCTREA ;READ A CHUNK OF MESSAGE
MOVE T3,TXTPTR(VR) ;FETCH POINTER TO IT
SCAND: SOSGE TXTLEN(VR) ;GET A CHARACTER HERE
JRST INLOOP ;NONE LEFT, GO REFIL BUFFER
ILDB T1,T3 ;PICK UP NEXT CHARACTER
CAIN T1,.CHLFD ;LF MEANS END OF LINE..
JRST FLAGLF ;SO GO FLAG IT AND GO ON
JUMPGE T6,DOTIRR ;ARE WE RIGHT AFTER A <LF>?
CAIE T1,"." ;YES, IS THIS A DOT?
JRST GOWRIT ;NO. CLEAR FLAG AND GO ON
MOVEI T6,1 ;YES, FLAG "<LF>." AND TOSS LEADING .
JRST SCAND ;AND GET NEXT CHARACTER
DOTIRR: JUMPE T6,GOWRIN ;NOT AFTER BARE <LF>. AFTER "<LF>."?
CAIE T1,.CHCRT ;ALLOW <LF>.<CR> TO TERMINATE
CAIN T1,.CHLFD ;ALSO ALLOW <LF>.<LF>, JIC
JRST WRISTR ;DONE! GO WRITE THIS LAST BUFFER
GOWRIT: TDZA T6,T6 ;HERE FOR A MUNDANE CHARACTER WRITE
FLAGLF: SETO T6, ;HERE TO FLAG A <LF>
GOWRIN: IDPB T1,OUTPNT(VR) ;PUT THE CHARACTER IN THE BUFFER
AOS T1,OUTCNT(VR) ;DID WE FILL IT?
CAIGE T1,OUTLEN*5-1 ;..
JRST SCAND ;NO, GET NEXT CHARACTER
;Here if we are dumping our last buffer (T6 .GT. 0) or have a full buffer to
;dump (T6 anything else).
WRISTR: SKIPE SENDFL(VR)
JRST SENDBR
PUSH P,FILE(VR) ;FILE
PUSH P,OUTBFR(VR) ;POINTER
PUSH P,OUTCNT(VR) ;LENGTH
PUSH P,[0] ;NO BUFFER FOR ERROR
CALL SVACLI
CALL UF%WRI
CALL RSACLI
ADJSP P,-4
TRNN T1,1
JRST STORFA ;WRITE ERROR, GO ABORT ALL THIS
SETOUT: SETZM OUTCNT(VR) ;BUFFER WRITTEN, RESET IT
MOVE T1,OUTBFR(VR)
MOVEM T1,OUTPNT(VR)
JUMPLE T6,SCAND ;"DONE" FLAG? GO DO MORE IF NOT LIT (.GT. 0)
EOFGOP: SKIPE SENDFL(VR)
JRST GOTHLO ;FINE, SAY OK
PUSH P,FILE(VR) ;CLOSE UP FILE
PUSH P,[0] ;CLOSE, NOT ABORT
PUSH P,[0] ;DONT WANT AN ERROR STRING
CALL SVACLI
CALL UF%CLO ;CLOSE IT
CALL RSACLI
ADJSP P,-3
SETZM FILE(VR) ;FILE NOT NOW OPEN
TRNN T1,1 ;CLOSE OK?
JRST STORFA ;NO, SAY FAILURE
;POST DATA AT REQ (LENGTH REQLEN)
logger <All DATA received, calling SCAN%P...>
PUSH P,REQ
PUSH P,[0]
PUSH P,[-1,,[ASCIZ/MX SMTP Listener/]]
PUSH P,[SC%WHL!IP$JAC]
CALL SVACLI
CALL SCAN%P ;CHECK THE PACKET
CALL RSACLI
ADJSP P,-4
logger <Return from SCAN%P>
JUMPE T1,STORFX ;SNH! WE BUILT IT RIGHT!
MOVE T1,.PKSTS(REQ) ;POST OK?
CAIN T1,.POST ;..
JRST GOTHLO ;RETURN OK
;BAD POST, ASSUME NO MEMORY TO HOLD IT
STORFX: PUSH P,SCNP%E
JRST STORFQ
SCNP%E: ASCIZ/ SMTLIS: Unexpected fail return from SCAN%P/
DSKW%E: ASCIZ/ SMTLIS: Can't write incoming mail to disk/
MEME%E: ASCIZ/ SMTLIS: Can't get memory to handle incoming mail/
STORFA: PUSH P,DSKW%E
JRST STORFQ
STORFL: PUSH P,MEME%E
;Here if a reasonable request can't be satisfied because of space
; Send a 452, Serious room problem.
;Come with address of complaint string on stack
STORFQ: CALL LOG
ADJSP P,-1
logger <STORFL, unable to find memory or disk space, returning 452>
$RSE (452,SP$UIE)
DATANO: CALL SNDRSP
DATAN1: CALL READY
JRST NXTCMD
SENDBR: SETZ T1,
IDPB T1,OUTPNT(VR) ;NULL TERMINATE
MOVE T1,RCPTPT
MOVEI T1,1(T1) ;POINT AT USERNAME TEXT
PUSH P,T1
PUSH P,OUTBFR(VR) ;POINTER TO MESSAGE
CALL SVACLI
CALL BROAD ;SEND IT TO THE TERMINALS
CALL RSACLI
ADJSP P,-2
JRST SETOUT
BROAD: RET ;FOR NOW
;Here if we get an unexpected command. See if it is in the spec at all,
; or legal but not in this context, or legal in this context but not handled by
; us, and give the right error accordingly. MX's SMTP sender should never allow
; this to get called.
WHAT: MOVEI T1,ALLTBL
CALL CMDFND ;Is it legal at all?
TRNA
JRST ERR503 ;NOPE
MOVEI T1,UNSTBL ;YES, DO WE JUST NOT SUPPORT IT?
CALL CMDFND
JRST ERR500
ERR502: $RSE (502,MG$UIC)
JRST SNDRSP
ERR503: $RSE (503,MG$UEC)
JRST SNDRSP
ERR500: $RSE (500,MG$UNC)
JRST SNDRSP
ALLTBL: ALEN,,ALEN
[ASCIZ/DATA/],,0
[ASCIZ/HELO/],,0
[ASCIZ/MAIL/],,0
[ASCIZ/NOOP/],,0
[ASCIZ/QUIT/],,0
[ASCIZ/RCPT/],,0
[ASCIZ/RSET/],,0
[ASCIZ/SOML/],,0
[ASCIZ/VRFY/],,0
ALEN=.-ALLTBL-1
;Unimplemented command
UNSTBL: ULEN,,ULEN
[ASCIZ/EXPN/],,0 ;WE AREN'T THIS BRIGHT
[ASCIZ/HELP/],,0 ;HELPLESS
[ASCIZ/SAML/],,0 ;WE NEVER DO SEND AND MAIL
[ASCIZ/SEND/],,0 ;WE NEVER DO SEND (THIS WAY)
[ASCIZ/TURN/],,0 ;WE AREN'T THIS BRIGHT
ULEN=.-UNSTBL-1
;Here if we know this node is going down real soon. Politely request we hang up.
SYSDWN: $RSP (221,This node is going down - please QUIT)
CALL SNDRSP
CALL DCTREA
MOVEI T1,TABLE4 ;SEVERELY LIMIT CORRECT RESPONSES
CALL CMDFND
JRST SYSDWN ;WRONG, COMPLAIN AGAIN
JRST (T1)
;LEGAL RESPONSE TO A '221 SORRY GOING DOWN NOW' MESSAGE
TABLE4: TLEN4,,TLEN4
[ASCIZ/QUIT/],,QUIT
TLEN4=.-TABLE4-1
;Take the first 4 characters at txtptr and see what command they are.
CMDFND: MOVE T2,@TXTPTR(VR)
LDB T3,[POINT 7,T2,34]
CAIE T3," " ;5TH CHAR MUST BE <SP> OR <CR>
CAIN T3,.CHCRT
ACCTAB: TRZA T2,377 ;OK. MAKE IT <NULL>
RET ;BAD CHARACTER, NO SKIP RETURN
MOVEM T2,CMDWRD(VR) ;STORE 4 SYMBOLS OF COMMAND AND <NULL>
PUSH P,T1 ;PUSH TABLE ADDRESS
MOVE T3,T1 ;KEEP TABLE ADDRESS AROUND
MOVE T1,[POINT 7,CMDWRD]
ADD T1,VR
PUSH P,T1 ;PUSH BP TO STRING
MOVEI T1,SCRWRD(VR) ;PUSH ADDRESS TO RETURN INDEX IN
PUSH P,T1 ;..
CALL SVACLI
CALL TBL%LO ;CALL TABLE LOOKUP ROUTINE
CALL RSACLI
ADJSP P,-3 ;TOSS STACKED ARGS
TRNN T1,1 ;OK MATCH?
RET ;NO MATCH
MOVE T1,SCRWRD(VR) ;INDEX WENT HERE
ADD T1,T3
HRRZ T1,(T1) ;GET RH OF TARGET WORD
CPOPJ1: AOS (P) ;SKIP HOME
RET
;HERE FROM STATE 1, WAITING FOR THE HELO COMMAND
OKGGRT: $RSP (250,Please HELO)
CALL SNDRSP
JRST GETGRT
;WE EXPECT A HELO AND WERE DISSAPOINTED
NOHELO: CALL WHAT
JRST GETGRT
;Return a null terminated string starting at TXTPTR(VR)
;On a channel death, dispatch to DIENOW (does not return)
DCTREA: PUSH P,DECNET
PUSH P,[MAXREC]
PUSH P,TXTPTR(VR)
CALL SVACLI
CALL UN%READ
CALL RSACLI
ADJSP P,-3
JUMPE T1,DCTREA ;NO ONE WANTS A NULL STRING
JUMPL T1,DCTERR ;-1 OR -2 IS ERROR
MOVEM T1,TXTLEN(VR)
ADJBP T1,TXTPTR(VR) ;FORCE NULL TERMINATION
SETZ T2,
IDPB T2,T1
logget TXTPTR(VR)
MOVE T1,TXTLEN(VR) ;RETURN LENGTH
CPOPJ: RET
DCTERR: AOJE T1,TIMOUT ;-1 MEANS TIMED OUT
;ERROR OTHER THAN TIMOUT. ASSUME MESSAGE TOO LONG FOR OUR BUFFER. IF,
; INSTEAD, IT IS AN ABORTED LINK, SNDRSP WILL GO TO TIMOUT
CHOKE: $RSE (500,MG$LTL)
CALL SNDRSP
ADJSP P,-1 ;UNDO CALL TO DCTREA
JRST DIENOW ;QUIT WHILE WE CAN
;Here with T1/ BP to directive string, T2/ number of args in TXTBLK
; formats and sends string
SNDRSP: PUSH P,RSPPTR(VR)
MOVEI T3,(P)
PUSH P,T3
PUSH P,[MAXREC-5]
TLC T1,-1
TLCN T1,-1
HRLI T1,(POINT 7)
PUSH P,T1
PUSH P,T2
PUSH P,TXTBLK(VR)
CALL SVACLI
CALL UT%TEX
CALL RSACLI
ADJSP P,-6
SOSG T4,T1
JRST STORFL
;JRST DNTOUT
;Needs T4 containing number of bytes to write, and RSPPTR(VR) pointing at
; text to write. On an error, does not return (goes to DIENOW)
DNTOUT: logget RSPPTR(VR)
PUSH P,DECNET
PUSH P,[1] ;COMPLETE MESSAGE
PUSH P,T4
PUSH P,RSPPTR(VR)
CALL SVACLI
CALL UN%WRI
CALL RSACLI
ADJSP P,-4
TRNN T1,1
JRST TIMOUT
RET
;Here when we see a QUIT
QUIT: $RSP (221,Closing channel)
CALL SNDRSP
logger <Received a QUIT, closing channel real soon>
JRST DIENOW
TIMOUT: logger <SINR failed (at TIMOUT)>
DIENOW: logger <At DIENOW>
CALL READY
;CLOSE DECNET
JUMPLE DECNET,NCLOND ;CLOSE IF OPEN
PUSH P,DECNET
PUSH P,[0]
PUSH P,[0]
CALL SVACLI
CALL UN%CLO
CALL RSACLI
ADJSP P,-3
NCLOND:
JRST GOAGAI ;GO AGAIN, WAIT FOR NEXT CONNECT
;COPY STRING AND END WITH NULL
CSTR: CALL CSTRB
IDPB T3,T2
RET
;MEASURE STRING, RETURN COUNT IN T4
STRLEN: TLCE T1,-1
TLCN T1,-1
HRLI T1,(POINT 7)
STRLN: SETZ T4,
STRLE1: ILDB T3,T1
CAIE T3,0
AOJA T4,STRLE1
RET
;COPY STRING, LEAVE POINTER SET FOR APPENDING
CSTRB: TLCE T1,-1
TLCN T1,-1
HRLI T1,(POINT 7)
TLC T2,-1
TLCN T2,-1
HRLI T2,(POINT 7)
CSTRC: ILDB T3,T1
JUMPE T3,CPOPJ
IDPB T3,T2
AOJA T4,CSTRC
;SAVE AND RESTORE AC'S FOR BLISS
RSACLI: DMOVE T2,SAVACL(VR)
DMOVE T4,SAVACL+2(VR)
RET
SVACLI: DMOVEM T2,SAVACL(VR)
DMOVEM T4,SAVACL+2(VR)
RET
NOMEME: PUSH P,[POINT 7,[ASCIZ/Out of memory at SMTLIS startup/]]
CALL NMLDIE
;NO RETURN
STDSTR: ASCIZ !%D %A%/!
STDSTI: ASCIZ !%D %I%/!
END