Title   VMAIL - MMAILR / TOPS-20 Mail-11 Listener
       Subttl  LCampbell/GStevens/DDyer-Bennet/MDLyons/JHTorrey/PLBudne/MRCrispin
       Search Macsym,Monsym
       .require Sys:Macrel
       Sall
       .directive Flblst
       .text "/NOINITIAL"
       .text "VMAIL/SAVE"
IFNDEF OT%822,OT%822==:1

; This  program  is  derived  from   the  VMAIL  distributed  on   the
; "integration tools tape".  It listens  on DECnet object 27 for  mail
; from the VMS MAIL Utility.  When a VMS user says "MAIL NODE::USER" A
; connection is established then and there, and text is sent one  line
; at a time.  A problem is that this server is single thread, and  one
; VMS user can  tie up  the port,  making all  other mail  to the  '20
; fail!!

; At the current time the addressee must be a valid TOPS-20  username,
; since the destination is stored as a user number.  I never  finished
; an interface to MMAILBOX to vaildate full MMAILR addresses.

; Another interesting problem is that there  may be no reply path  for
; mail.  I had thought of taking the VMAILR program and hacking it  to
; be the delivery agent for a #Special domain.  At BU we run  software
; tools mail on most VMS systems, so this is not a problem.

IFNDEF FTRCVD,FTRCVD==1         ;INCLUDE RECIEVED: HEADER
IFNDEF MBXF,MBXF==0             ;INCOMPLETE!!
IFN MBXF,<
       .REQUIRE VMAMBX         ;INTERFACE TO MMAILBOX
       EXTERNAL MBXFRK,MBXVFY
> ;MBXF

T1==1
T2==2
T3==3
T4==4
T5==5
P1==6
P2==7
P3==10
P4==11
P5==12
Ptr==13                         ;Global Byte Pointer To Receive Mail
Cnt==14                         ;Global Byte Count For Same
Cx==16
P==17

ver==6
mvr==1
edt==^D82

       Loc 137
       Exp <.ver>B11+<.mvr>B17+1B18+.edt
       Reloc

Define Jerr(String),<
       Xlist
       IFJER.
         Hrroi T1,[Asciz /VMAIL error: /]
         Esout
         Hrroi T1,[Asciz /String/]
         Psout
         Hrroi T1,[Asciz / because: /]
         Psout
         Movx T1,.priou
         Hrloi T2,.fhslf
         Erstr
          Erjmp .+2
          Erjmp .+1
         Call Dtstmp   ;; Log This Lossage Also
         Log <String>
         Log < Because: >
         Move T1,LogJFN
         Hrloi T2,.fhslf
         Erstr
          Erjmp .+2
          Erjmp .+1
         Jrst Fatal
       ENDIF.
       List
>

Define Log(String),<            ;; Put Message Into Log File
       Xlist
       Hrroi T1,[Asciz \String\] ;; So It Can Type Slashes
       Call Logmsg
       List
>

Define Debug(String),<
       Skipn Dbugsw
       IFSKP.
         Hrroi T1,[Asciz /String/]
         Psout
       ENDIF.
>

Define Debstr(String),<
       Skipn Dbugsw
       IFSKP.
         Hrroi T1,String
         Psout
       ENDIF.
>

Define Clrbuf(bufnam,Buflen),<
       Setzm Bufnam
       Move  T1,[Bufnam,,Bufnam+1]
       Blt   T1,Bufnam+buflen-1
>

Define Nchar,<
       Move T1,NetJFN
       Bin
       Move T1,T2
>

Define Nrecord(Buffer,Nchar),<
       Move  T1,NetJFN
       Hrroi T2,Buffer
       Movni T3,Nchar
       Setz  T4,
       Sinr
        Jerr <SINR failed at Nrecord>
>

Define Vaxsuccess,<
       Move  T1,NetJFN
       Hrroi T2,[Ascii//]
       Movei T3,-4
       Setz  T4,
       Soutr
>

Define Vaxerr(Errmsg),<
       Move  T1,NetJFN
       Hrroi T2,[Ascii//]
       Movei T3,-4
       Setz  T4,
       Soutr

       Move  T1,NetJFN
       Hrroi T2,[Asciz /Errmsg/]
       Setzb T3,T4
       Sout

       Hrroi T2,Atmbuf
       Setzb T3,T4
       Sout

       Hrroi T2,[Asciz/ At Node /]
       Setzb T3,T4
       Sout

       Hrroi T2,ournam
       Setzb T3,T4
       Soutr                   ; force string transmission

       Hrroi T2,[0]
       Movei T3,-1
       Setz  T4,
       Soutr
>

Define Die(String),<            ;; Fatal Internal Error
       Xlist
       Jrst [  Hrroi T1,[Asciz /VMAIL fatal internal error: /]
               Esout
               Hrroi T1,[Asciz /String/]
               Psout
               Hrroi T1,[Asciz /
/]
               Psout
               Call Dtstmp     ;; Time Stamp It
               Hrroi T1,[Asciz /Fatal error: /]
               Call Logmsg
               Hrroi T1,[Asciz /String/]
               Call Logmsg
               Jrst Fatal]
       List
>

Define Herald(Ver,Edt),<
       Xlist
;       Tmsg <VMAIL version Ver(Edt) running>
       Hrroi T1,[Asciz /VMAIL version Ver(Edt) running/]
       Call Logmsg
       List
>

Define Log(String),<
       Xlist
       Hrroi T1,[Asciz \String\]
       Call Logmsg
       List
>

;Storage
Tmplen==500                     ; Temporary Storage
Natmbf==100                     ; Length Of Atom Buffer In Words
Bbflen==300000                  ;[154] Length Of Big Mail Buffer
Nfrmbf==70                      ; Length Of Sender Name Buffer
Timen==^D<10*60*1000>           ; Milliseconds Before Sender Declared Tardy
Stklen==200                     ; Size Of Stack

Dbugsw: 0                       ; -1 If Debug
Atmbuf: Block Natmbf            ; Atom Buffer
Subbuf: Block Natmbf            ; Subject Buffer
Toocnt: Block 1                 ; Count of recipients per line in TOOBUF
Tooptr: Block 1                 ; Pointer to current position in TOOBUF
Toobuf: Block Natmbf            ; To Names Buffer
Bigbuf: Block Bbflen            ; Where It All Is Combined To
Nodstr: Block ^D200             ; String space for recipient nodes/USERS
NODPTR: BLOCK 1                 ;[BUDD] BP INTO NODSTR
Ulist:  Block ^D200             ; Where To Store Mailbox Directory Numbers
Frmbuf: Block Nfrmbf            ; Where To Put Sender'S Name Plus Host
Frmnam: Block Nfrmbf            ; Where To Put Sender'S Name
Ournam: Block 2                 ; Our Host Name
Hstnam: Block 2                 ; Host name we are sending to
Usrnum: Block 1                 ; User number we are sending to
MYPID:  BLOCK 1                 ;[BUDD] OUR IPCF PID
BUFFER: BLOCK 10                ;[BUDD] IPCF BUFFER
IPCBLK: BLOCK 4                 ;[BUDD] IPCF DESCR BLOCK
OURJOB: BLOCK 1                 ;[BUDD] OUR JOB NUMBER
QUEJFN: BLOCK 1                 ;[BUDD] MMAILR QUEUE JFN
Filnam: Block 1                 ;[BUDD] File name for MMAILR QUEUE FILE
Temp1:  Block Tmplen
Temp2:  Block Tmplen
Stack:  Block Stklen            ; One Stack For Each Fork
NetJFN: Block 1                 ; Network File JFN
LogJFN: Block 1                 ; Log File JFN
LOGP:   BLOCK 1                 ; Non-zero if keeping logs
Ntime:  Block 1                 ; Time Receipt Of Mail Initiated (For Status)
Elptim: Block 1                 ; Elapsed Time For Receipt Of Mail
Kepliv: Block 1                 ; Keep alive count for dead mans timer
Bytcnt: Block 1                 ; Length Of Mail In Bytes
Capenb: Block 1                 ; Saved Capabilities

IFN MBXF,<                      ;[BUDD]
GOTMBX: BLOCK 1                 ;[BUDD] -1 IF HAVE MBX FORK
> ;[BUDD] MBX

Pc1:    Block 1                 ; Pc Save Location For Psi Level
Pc2:    Block 1
Pc3:    Block 1
Levtab: Pc1
       Pc2
       Pc3
Chntab: 2,,Conect               ; Connect Initiate On Level 2
       1,,Timout               ; Timeout Psi On Level 1
       Xlist                   ; Nothing Else
       Repeat ^d34,<Exp 0>
       List

VMAIL:: Reset
       Move P,[-Stklen,,Stack]
       GJINF                   ;[BUDD]
       Movem T3,OurJob         ;[BUDD]

       Movx T1,.ndgln          ; Get Local Node Name Function
       Move T2,[Point 7,Ournam]
       Movem T2,1(P)           ; Put Pointer On Stack
       Movei T2,1(P)           ; And Point To It
       Node                    ; Get Node Name
       ifjer.
         tmsg <?No DECnet in this monitor>
         haltf%
       endif.

IFN MBXF,SETZM GOTMBX           ;[BUDD] CLEAR MBX AVAIL FLAG

       Movx T1,.fhslf          ; This Process
       Move T2,[Levtab,,Chntab]
       Sir                     ; Init Psi System
       Eir
       Call Opnlog             ; Open Log File
       Movem T1,LogJFN         ; Save JFN
       Call Dtstmp             ; Time Stamp It
       Herald \.ver,\.edt
       Log< on node >
;       Tmsg < on node >
;       Hrroi T1,Ournam
;       Psout
;       Tmsg <
;>
       Hrroi T1,Ournam
       Call Logmsg
VMAIL0: Call Opnlsn             ; Open Connection And Set Interrupt Up
       Move T1,LogJFN          ; Close Log File For Perusers
       Closf
        Erjmp .+1
       Wait                    ; For Connect Initiate

;Here When Connection Initiated

Conect: Move P,[-Stklen,,Stack] ; Reset Stack
       Call Timeit             ; Time This Guy
       Call Opnlog             ; Open Log File
       Movem T1,LogJFN
       Call Dtstmp             ; Time Stamp This Transaction
       Log <----Connect from >
       Debug <----Connect from >
       Call T4nhst             ; Type Foreign Host Name At Log File

       Clrbuf Subbuf,Natmbf

       Move  T1,NetJFN         ; Accept Connection
       Movx  T2,.mocc
       Setzb T3,T4             ; No Additional Data
       Mtopr
        Jerr <Couldn't accept net connection>
Conct1: Move  T1,NetJFN         ; Get network link status
       Movei T2,.morls
       Mtopr
        Jerr <Couldn't get link status>
       TXNE    T3,MO%ABT       ; [154] Has the link been aborted?
         JRST  DMPLNK          ; [154]   Yes, get rid of it.
       Txne  T3,Mo%con         ; Skip if link not connected
       Jrst  Conct2
       Movei T1,^D1000         ; Wait a second and try again
       Disms
       Jrst  Conct1

Conct2: Movx T1,.hpelp          ; Elapsed Time Since System Startup
       Hptim                   ; Snarf It
        Jerr <HPTIM failed>
       Movem T1,Ntime          ; Remember Time This Reception Started
       Call Parse              ; Parse The Mail
        Jrst Errxit            ; Failed, Quit Now
       Call Dtstmp             ; Time Stamp Log
       Log <Message from >
       Hrroi T1,Frmbuf         ; Sender'S Name
       Call Logmsg             ; Log It
       Log < received >
       Call Lstats             ; Log Statistics
       Call Mailit             ; Send The Mail Off
        Die <Failure returned from MAILIT>

Errxit: Call Clznet             ; Close And Reopen Net Link
       Call Cncltm             ; Cancel Timeout Request
       Call Dtstmp
       Log <----Connection closed>
       Move T1,LogJFN
       Closf                   ; Close Log File For Perusers
        Erjmp .+1
       Debrk                   ; Return To Background

; Parse Mail Received.  Place Sender Name In Frmbuf, Recipient Directory
; Numbers In Ulist, Terminated With A Zero Entry
; Headers Must Appear In The Following Order.
;                       From, To, Cc
; Returns +1: Failure
;         +2: Success
;
;PROGRAM FLOW DESCRIPTION NOT ALL ITEMS IN FLOW ARE IN THIS ROUTINE BUT
;IT DOES REPRESENT THE PROCEDURE TO SEND TO A VAX WHICH IS WHY IT IS
;INCLUDED HERE
;
;       RECEIVE FROM FIELD FROM VAX
;       PARSE FROM FIELD CONVERTING IT TO MS TYPE FIELD IN FRMBUF
;       REPEAT UNTIL NULL RECEIVED
;       :  RECEIVE A RECIPIENT NAME FOR VERIFICATION
;       :  IF NULL RECEIVED
;       :  :  THEN
;       :  :  :  EXIT REPEAT LOOP
;       :  ENDIF
;       :  PARSE USER NAME AND NODE
;       :  IF NODE SAME AS THIS NODE
;       :  :  THEN
;       :  :  :  IF USER IS ON THIS SYSTEM
;       :  :  :  :  THEN
;       :  :  :  :  :  SEND SUCCESS CODE TO VAX
;       :  :  :  :  :  PUT USER NUMBER INTO ULIST
;       :  :  :  :  ELSE
;       :  :  :  :  :  SEND FAILURE CODE TO VAX
;       :  :  :  :  :  SEND ERROR MESSAGE TO VAX
;       :  :  :  :  :  SEND NULL TERMINATING ERROR MESSAGE TO VAX
;       :  :  :  :  :  RETURN FROM ROUTINE
;       :  :  :  ENDIF
;       :  :  ELSE
;       :  :  :  SEND SUCCESS TO VAX (MESSAGE WILL BE QUEUED)
;       :  :  :  PUT -1 INTO ULIST
;       :  :  :  PUT NODE NAME INTO NODLST
;       :  ENDIF
;       END REPEAT
;       RECEIVE TO FIELD FROM VAX
;       PARSE TO FIELD CONVERTING IT TO MS TYPE FIELD IN TOOBUF
;       RECEIVE SUBJECT FIELD FROM VAX
;       BEGIN FORMATING MESSAGE INTO MS TYPE MESSAGE
;       REPEAT UNTIL NULL RECEIVED
;       :  RECEIVE A LINE FROM VAX
;       :  IF NULL RECEIVED
;       :  :  THEN
;       :  :  :  EXIT REPEAT LOOP
;       :  ENDIF
;       :  OUTPUT TO MS MESSAGE BUFFER
;       END REPEAT
;       REPEAT UNTIL NULL DETECTED
;       :  GET FIRST ITEM IN ULIST
;       :  IF FIRST ITEM IN ULIST = -1
;       :  :  THEN
;       :  :  :  GET NODE FROM NODLST
;       :  :  :  PUT MAIL INTO FILE FOR DMAILR
;       :  :  :  SET FLAG IN DECNET-FLAGS SO FILE GETS SENT
;       :  :  ELSE
;       :  :  :  PUT MAIL INTO USERS MAIL FILE
;       :  :  :  SPLAT OBNOXIOUS MESSAGE ACCRESS USERS SCREEN
;       :  ENDIF
;       :  IF NO ERROR
;       :  :  THEN
;       :  :  :  SEND POSITIVE ACKNOWLEDGEMENT TO VAX
;       :  :  ELSE
;       :  :  :  SEND NEGATIVE ACKNOWLEDGEMENT TO VAX
;       :  :  :  SEND ERROR MESSAGE TO VAX
;       :  :  :  SEND NULL TERMINATOR TO VAX
;       :  ENDIF
;       END REPEAT
;       RETURN +2

Parse:  Clrbuf Frmnam,Nfrmbf

       Move  T1,NetJFN                 ; Save It
       Movei T2,.morss                 ; Read Max Record Size
       Mtopr
         Jerr <Couldn't read max record size>  ; This could fail...

       Nrecord <Frmnam>,<Nfrmbf*5-1>   ; Read From Field

       Hrroi T1,Temp1                  ; Setup Default Host
       Hrroi T2,Hstnam
       Setzb T3,T4
       Sout
       Hrroi T1,Frmbuf                 ;Parse from field, results to FRMBUF
       Move  T2,[Point 7,Frmnam]
       Call  Prsnam

       Setzm Toobuf            ; Clear first location of TOOBUF
       Hrroi T1,Toobuf         ; Setup pointer to TOOBUF
       Movem T1,Tooptr
       Movei T1,3              ; Setup count of recipients per line in TOOBUF
       Movem T1,Toocnt
       MOVEI T1,NODSTR         ;[BUDD]
       Movem T1,NodPTR         ;[BUDD]
       Movsi P1,-^D100         ; Maximum Of 100 Names In List

Parse3: Clrbuf Atmbuf,Natmbf    ; Clear receive area
       Nrecord <Atmbuf>,<Natmbf*5-1> ; Receive recipient from VAX
       Aos   Kepliv            ; Increment keep alive count
       Skipn Atmbuf            ; Skip if not end of list
       Jrst  Parse6            ;   End of recipient list
       Call  Prsusr            ; Parse recipient
        Jrst [ Call Dtstmp             ; None Found, Complain
               Vaxerr <%Network mail error: No such user >
               Log <%Network mail error: No such user >
               Hrroi T1,Atmbuf         ; Also Log Losing Name
               CallRet Logmsg]
       Movem T1,Ulist(P1)      ; Save number returned for mailing
       Vaxsuccess              ; Send VAX the success code
       Aobjn P1,Parse3         ; Jump if not too many recipients

       Call Dtstmp             ; Woops, Too Many
       Hrroi T1,Atmbuf         ; Also Tell Log File
       CallRet Logmsg

Parse6: Setzm Ulist(P1)         ; Tie Off Recipient List

;
; Now Get Mailed To Field With Node Name And Subject
;

       Clrbuf Atmbuf,Natmbf
       Nrecord <Atmbuf>,<Natmbf*5-1>
       Nrecord <Subbuf>,<Natmbf*5-1>

; Now Conbine It All Into Bigbuf

       Setzm Bytcnt
       Hrroi T1,Bigbuf
IFN FTRCVD,<                    ;[BUDD]
       Hrroi T2,[Asciz /Received: from /] ;[BUDD] now, write Received line
       Setz T3,                ;[BUDD]
       SOUT                    ;[BUDD]
       Hrroi T2,HSTNAM         ;[BUDD]
       SOUT                    ;[BUDD]
       HRROI T2,[ASCIZ ' by '] ;[BUDD]
       SOUT                    ;[BUDD]
       HRROI T2,OURNAM         ;[BUDD]
       SOUT                    ;[BUDD]
       Hrroi T2,[Asciz ' using MAIL-11 '] ;[BUDD]
       SOUT                    ;[BUDD]
       Seto T2,                ;[BUDD] output current date/time
       MovX T3,OT%DAY!OT%SPA!OT%TMZ!OT%SCL!OT%822
       ODTIM                   ;[BUDD] RFC 822 standard date
        JERR <ODTIM in recieved line failed> ;[BUDD]
       Hrroi T2,CRLF0          ;[BUDD]
       Setz T3,                ;[BUDD]
       SOUT                    ;[BUDD]
> ;IFN FTRCVD
       Hrroi T2,[Asciz /Date: /]
       Setz T3,
       Sout
       Seto  T2,
       MovX T3,OT%DAY!OT%SPA!OT%TMZ!OT%SCL!OT%822
       Odtim
        Jerr <Couldn't get current date-time at PARSE6+n>

       Hrroi T2,[Asciz/
From: /]
       Setz T3,
       Sout

       Hrroi T2,Frmbuf
       Setz T3,
       Sout

       Hrroi T2,[Asciz/
To: /]
       Setzb T3,T4
       Sout

       Hrroi T2,Toobuf
       Setzb T3,T4
       Sout

       Hrroi T2,[Asciz/
Subject: /]
       Setzb T3,T4
       Sout

       Hrroi T2,Subbuf
       Setzb T3,T4
       Sout

       Hrroi T2,[Asciz/
Mailed-to: /]                   ;[BUDD]
       Setzb T3,T4
       Sout

       Hrroi T2,Atmbuf
       Setz T3,
       Sout

       Hrroi T2,CRLF0
       SOUT
       Hrroi T2,CRLF0
       SOUT

       Move  T5,T1
Repeat: Clrbuf Temp1,Tmplen     ; Clear storage area
       Nrecord <Temp1>,<Tmplen*5-1> ; Get a message line from the VAX
       Aos   Kepliv            ; Increment keep alive count
       Camn  T3,[-<tmplen*5-1>] ; Blank line ?
        Jrst Crlf              ; Yes, output CRLF
       Skipn Temp1             ; End of message ?
        Jrst Done              ; Yes
       Move  T1,T5             ; Output message line to BIGBUF
       Hrroi T2,Temp1
       Setzb T3,T4
       Sout
         Jerr <SOUT failed at REPEAT+a few> ; [151] Add error check
       Move  T5,T1
Crlf:   Move  T1,T5             ; Output CRLF to BIGBUF
       Hrroi T2,[Asciz/
/]
       Setzb T3,T4
       Sout
        Jerr <SOUT failed at CRLF+3> ;[152] Add error check
       Move  T5,T1
       Jrst Repeat

Done:   Move  T1,T5             ; Close off message
       Hrroi T2,[Asciz /
/]
       Setzb T3,T4
       Sout

       Call Gtbfsz             ; Get Buffer Size Into Bigbug
       Debug <
>
       Debstr <Bigbuf>

       Retskp                  ; All Done!

;
;Store Size Of Bigbuf Into Bytcnt
;

Gtbfsz: Setz  T3,
       Setz  T2,
       Move  T1,[Point 7,Bigbuf]
Geta:   Ildb  T2,T1
       Jumpe T2,Go
       Addi  T3,1
       Jrst  Geta
Go:     Movem T3,Bytcnt
       Jrst  R

;Routine to parse a node and user name and convert it to a TOPS-20
;string compatable with MS
;
;CALL:
;       T1       = STRING POINTER TO THE DESTINATION
;       T2       = STRING POINTER TO FIELD RECEIVED
;       CALL PRSNAM
;
;VARIABLES RETURNED ON SUCCESSFUL COMPLETION
;       DESTINATION CONTAINS THE MS STRING
;       TEMP1  = HOST
;       TEMP2  = USER NAME
;       T1       = UPDATED STRING POINTER
;
;RETURNS:
;       +1 ALWAYS

PRSNAM:
       ACVAR  <SPTR,DPTR>      ; STORAGE FOR SOURCE/DEST. POINTERS
       MOVEM T1,DPTR           ; SAVE DESTINATION POINTER
       MOVEM T2,SPTR           ; SAVE SOURCE POINTER
PRSNM1: MOVE  T1,[POINT 7,TEMP2] ; GET POINTER TO WHERE TO STORE STRING
PRSNM2: ILDB  T3,SPTR           ; GET CHARACTER
       JUMPE T3,PRSNMD         ; NULL, DONE
       CAIN  T3," "            ; SPACE SEPARATOR?
       JRST  PRSNM3            ; YES, LOOK FOR PERSONAL NAME
       CAIE  T3,":"            ; END OF NODE SPECIFIER?
       IFSKP.
         ILDB  T3,SPTR         ; YES, EAT NEXT COLON
         SETZ  T3,             ; TERMINATE STRING
         IDPB  T3,T1
         HRROI T1,TEMP1        ; UPDATE ORIGINATING HOST
         HRROI T2,TEMP2
         SOUT
         JRST  PRSNM1
       ENDIF.
       IDPB  T3,T1             ; SAVE CHARACTER IN TEMP2
       JRST  PRSNM2

PRSNM3: SETZ  T3,               ; TERMINATE STRING
       IDPB  T3,T1
PRSNM4: ILDB  T3,SPTR           ; GET CHARACTER FROM INPUT STRING
       JUMPE T3,PRSNMD         ; STRING EXHAUSTED
       CAIE  T3,42             ; IS CHARACTER START OF PERSONAL NAME?
       JRST  PRSNM4            ; NO, LOOP TILL STRING EXHAUSTED OR '"' FOUND
       MOVE  T1,DPTR           ; GET DESTINATION POINTER
       MOVE  T2,SPTR           ; GET SOURCE POINTER
       SETZ  T3,
       MOVEI T4,42             ; TILL '"'
       SOUT
       MOVEI T2," "            ; CHANGE '"' TO SPACE
       DPB   T2,T1
       MOVEI T2,"<"            ; OUTPUT START OF USERNAME at NODE STRING
       IDPB  T2,T1
       CALL  PRSNMS            ; CALL ROUTINE TO OUTPUT USERNAME at NODE
       MOVEI T2,">"            ; OUTPUT END OF USERNAME at NODE STRING
       IDPB  T2,T1
       SETZ  T2,               ; TERMINATE THE OUTPUT STRING
       IDPB  T2,T1
       RET                     ; SUCCESS RETURN

PRSNMD: IDPB    T3,T1           ; TERMINATE STRING
       MOVE    T1,DPTR         ; BUILD STRING IN DESTINATION BUFFER
       CALLRET PRSNMS
       ENDAV.

;Routine to create user name at node string in output buffer.
;
;CALL:
;       T1      = STRING POINTER TO WHERE TO STORE THE OUTPUT
;       TEMP2   = USER NAME
;       TEMP1   = NODE NAME
;       CALL PRSNMS
;
;VARIABLES RETURNED:
;       T1      = UPDATED STRING POINTER
;
;RETURNS:
;       +1 ALWAYS

PRSNMS: HRROI T2,TEMP2          ; OUTPUT USER NAME
       SETZB T3,T4
       SOUT
;;      HRROI T2,[ASCIZ/ at /]  ; OUTPUT SEPARATOR
;;      SOUT
       MOVEI T2,"@"
       BOUT
       HRROI T2,TEMP1          ; OUTPUT NODE NAME
       SOUT
       HRROI T2,[asciz/.#DECnet/]
       SOUT
       RET                     ; SUCCESS RETURN

;Routine to parse addressing of VAX mail and build TO string in TOOBUF
;
;CALL:
;       ATMBUF = ADDRESS STRING RECEIVED FROM THE VAX
;       CALL PRSUSR
;
;VARIABLES RETURNED ON SUCCESS:
;       TEMP1  = NODE OF RECIPIENT
;       TEMP2  = NAME OF RECIPIENT
;       T1       = USER NUMBER IF ON CURRENT NODE OR
;                PTR TO NODE // '\0' // USER FOR 4N HOST
;
;RETURNS:
;       +1: ERROR, MAIL WAS ADDRESSED TO THIS NODE AND USER WAS UNKNOWN
;       +2: OK, ALL RETURNED VARIABLES VALID

PRSUSR:
       MOVE  T1,TOOPTR         ; GET POINTER TO TOOBUF
       SKIPN TOOBUF            ; SKIP IF TOOBUF NOT EMPTY
        JRST  PRSUS1
       SOSE  TOOCNT            ; SUBTRACT FROM COUNT/LINE - SKIP IF .NE. 0
       IFSKP.
         HRROI T2,[BYTE (7) ",", "M"-100, "J"-100, "I"-100]
         SETZ T3,
         SOUT
         MOVEI T2,3            ; RESET COUNT OF USERS PER LINE IN TOOBUF
         MOVEM T2,TOOCNT
         JRST  PRSUS1
       ENDIF.
       HRROI T2,[ASCIZ/, /]    ; OUTPUT SEPARATOR
       SETZ T3,
       SOUT

PRSUS1: MOVEM T1,TOOPTR         ; SAVE STRING DESTINATION POINTER
       HRROI T1,TEMP1          ; SETUP DEFAULT HOST
       HRROI T2,OURNAM
       SETZ T3,
       SOUT
       MOVE  T1,TOOPTR         ; GET DESTINATION POINTER
       MOVE  T2,[POINT 7,ATMBUF] ; GET POINTER IN INPUT STRING
       CALL  PRSNAM            ; GET TOPS-20 MS STRING
       MOVEM T1,TOOPTR         ; SAVE POINTER TO TOOBUF

       HRROI T1,OURNAM         ; GET POINTER TO THIS SYSTEMS NODE NAME
       HRROI T2,TEMP1          ; GET HOST NAME FROM FIELD
       STCMP                   ; IS MESSAGE FOR THIS HOST ?
       JUMPE T1,PRSUS2         ; JUMP IF FOR THIS HOST

;MAIL IS FOR A REMOTE HOST.  GET HOST,,USER STRING POINTERS
       HRRZ T4,NODPTR          ;GET POINTER TO STRING SPACE
       HRRO T1,T4              ;PUT NODE NAME INTO NODE STRING SPACE
       HRROI T2,TEMP1
       SETZ T3,
       SOUT
       IDPB T3,T1              ;GET TERMINATOR
       HRROI T2,TEMP2          ;GET USER
       SOUT
       IDPB T3,T1
       HRRZM T1,NODPTR

       MOVE T1,T4              ;GET STRING POINTER
       RETSKP                  ; RETURN ADDRESS

;MAIL IS FOR THIS SYSTEM.  CHECK TO SEE IF USER NAME IS VALID.

PRSUS2:
IFE MBXF,<
       HRROI T2,TEMP2          ; POINT TO USER NAME STRING
       MOVX  T1,RC%EMO         ; EXACT MATCH ONLY
       RCUSR                   ; IS THIS USER NAME VALID ?
        ERJMP R                ; NO, ERROR
       TXNE  T1,RC%NOM         ; SKIP IF USERNAME FOUND
        RET                    ; NO SUCH USER - ERROR
       MOVE T1,T3              ; RETURN USER NUMBER IN T1
> ;IFE MBXF
IFN MBXF,<
PRINTX  NEED CODE AT PRSUS2
> ;IFN MBXF
       RETSKP                  ; RETURN SUCCESS

; Here To create MMAILR queue file

; Returns +1: Problems Of Some Sort
;         +2: Ok

Mailit: Hrroi T1,FILNAM
       Hrroi T2,[Asciz 'MAILQ:[--QUEUED-MAIL--].NEW-']
       Setz T3,
       SOUT
       Move T2,T1
       GTAD
       Exch T1,T2
       Movei T3,10
       NOUT
        Trn
       Hrroi T2,[Asciz '-VMAIL-J']
       Setz T3,
       SOUT
       Move T2,OURJOB
       Move T3,[3,,^D10]
       NOUT
        Trn
       Hrroi T2,[Asciz '.-1;P770000']
       Setz T3,
       SOUT
       Idpb T2,T3
       Movsi T1,(GJ%SHT+GJ%FOU)
       Hrroi T2,FILNAM
       GTJFN
        JERR <Could not get queue JFN>
       Movem T1,QUEJFN
       Move T2,[fld(7,OF%BSZ)+OF%WR]
       OPENF
        JERR <Could not open queue JFN>
       Hrroi T2,[Asciz ' =NET-MAIL-FROM-HOST:']
       Setz T3,
       SOUT
       Hrroi T2,HstNam
       SOUT
       Hrroi T2,CRLF0
       SOUT

       Hrroi T2,[Asciz ' =RETURN-PATH:']
       SOUT
       Hrroi T2,FRMBUF
       SOUT
       Hrroi T2,CRLF0
       SOUT

;; This is not documented -- but it makes MMAILR splat your TTY right!
       Hrroi T2,[Asciz ' _']
       SOUT
       Hrroi T2,HstNam
       SOUT
       Hrroi T2,[ASCIZ/.#DECnet
/]
       SOUT
       Move T3,[Point 7,FrmNam]
       DO.
         Ildb T2,T3
         Jumpe T2,ENDLP.
         Cain T2," "
          EXIT.
         BOUT
         LOOP.
       OD.
       Hrroi T2,CRLF0
       Setz T3,
       SOUT

       Setzb P1,P2             ; Init Index And Failure Flag
Mailt1: Move  T1,Ulist(P1)      ; Get Next Recipient
       Jumpe T1,Mailt5         ; End Of List
       Call  Sendit            ; ADD NAME TO TOP OF QUEUE FILE
        TRN
Mailt2: Vaxsuccess
       Aoja  P1,Mailt1

Mailt5: Jumpn P2, Rskp          ; Don'T Log Success on error
;;      Skipe P1                ; Anything Sent?
;;      IFSKP.
;;        Call Dtstmp           ; Yes, Loc Lack Of Local Users
;;        Log <No local electronic recipients>
;;      ENDIF.
       Move T1,QUEJFN          ; Get MMAILR queue JFN
;;[76]  Hrroi T2,CRLF0
;;[76]  Setz T3,
;;[76]  SOUT                    ;Sendit now has been fixed!!
       Movei T2,"L"-100
       BOUT
       Hrroi T2,CRLF0
       SOUT
       Move T2,[Point 7,Bigbuf] ; Shove message into queue file
       Movn T3,Bytcnt          ; Get negative byte count
       SOUT
        Erjmp .+1
       CLOSF
        TRN
       CALL WAKEUP
       Retskp

; Append Mail To User'S Mail File
; Call With User Number Of Recipient In T1

Sendit: Stkvar <Usrno>
       Movem T1,Usrno          ; Save Recipients User Number
       Jumpg T1,Quefil         ; 4n host

       Move T1,QueJFN          ; Get QUEUE file
       Movei T2,"L"-100        ; Get <FF>
       BOUT
       Hrroi T2,OurNam
       Setz T3,
       SOUT
       Hrroi T2,CRLF0
       SOUT
       Move T2,UsrNo
       DIRST
        ERJMP .+1              ;[76] DIRST% always gives +1 return
       Hrroi T2,CRLF0
       SOUT
       Retskp

;Sending to a remote host.
Quefil: MOVE T1,QUEJFN
       MOVEI T2,"L"-100
       BOUT
       HRRO T2,USRNO           ;GET HOST BP
       SETZ T3,
       SOUT
       MOVEM T2,USRNO          ;SAVE BP TO USER
       HRROI T2,CRLF0
       SOUT
       MOVE T2,USRNO           ;GET USER BP
       SOUT
       HRROI T2,CRLF0
       SOUT
       RETSKP                  ;all done

;Here to copy (and quote) "from" string into area pointed to by T1
; Quotes all characters (to save trouble of checking need for it)

QUOTE:  MOVE T2,[POINT 7,FRMBUF]
       TLC T1,-1               ; lh of byte pointer all ones?
       TLCN T1,-1              ;  ..
       HRLI T1,(POINT 7,)      ; yes, make real byte pointer
       MOVEI T4,<24*5>-1       ; maximum characters allowed in string
QUOTE1: MOVEI T3,"V"-100        ; quote character
       IDPB T3,T1              ; stuff it
       ILDB T3,T2              ; next char of source string
       IDPB T3,T1              ; stuff it
       JUMPE T3,[MOVNI T2,1            ; if zero, back up over last ctrl-V
               ADJBP T2,T1             ;  ..
               DPB T3,T1               ; wipe it out with null
               RET]                    ; and return
       SOJGE T4,QUOTE1         ; insure no overflow
       DIE <QUOTE overflow>

;Open Log File

Opnlog: MOVX T1,.NULIO          ; No logs unless
       SKIPN LOGP              ;  LOGP is set non-zero
        RET                    ; Oh well
       Movx  T1,Gj%sht         ; Try Logical Name First
       Hrroi T2,[Asciz /MAIL:VMAIL.LOG/]
       GtJFN
        Erjmp Opnerr
       Movx  T2,<070000,,0>+Of%app
       Openf                   ; Open For Append
        Erjmp Opnerr
       Ret

Opnerr: Hrroi T1,[Asciz /VMAIL: Can't open log file because: /]
       Esout
       Movx  T1,.priou
       Hrloi T2,.fhslf
       Erstr
        Erjmp .+1
        Erjmp .+1
       Jrst Fatal

;Time Stamp Log File

Dtstmp: Move  T1,LogJFN
       HRROI T2,[ASCIZ/
/]
       SETZ T3,
       SOUT%
        Jerr <Can't write to log file>
       Seto  T2,               ; Current Time
       Odtim
       IFJER.
         Hrroi T1,[Asciz /VMAIL: Odtim Failed: /]
         Esout
         Movx  T1,.priou
         Hrloi T2,.fhslf
         Erstr
          Erjmp .+2
          Erjmp .+1
         Tmsg <
DTSTMP called from >
         Movx  T1,.priou               ; Type Pc Of Caller On Terminal
         Hrrz  T2,(P)
         Movx  T3,^D8          ; In Octal
         Nout
          Erjmp .+1
         Jrst  Fatal           ; Go Fire Up The World Again
       ENDIF.
       Movei T2," "            ; Space
       Bout
       Ret

;Write Asciz String Pointed To By T1 To Log File

Logmsg: Move  T2,T1             ; Copy String Pointer
       Move  T1,LogJFN
       Setzb T3,T4
       Sout
        Jerr <Can't write to log file>
       Move  T1,T2
       Ret


;Write Statistics To Log File

Lstats: Stkvar<Elptm0>
;       Move  T1,LogJFN
;       Move  T2,Elptim         ; Elapsed Time For Mail Receipt
;       Fltr  T2,T2             ; Flost It
;       Fdvr  T2,[100000.0]     ; Compute Seconds
;       Movx  T3,<1b1+Fl%one+Fl%pnt+3b23+3b29>
;       Flout                   ; Type Seconds
;        Erjmp [Haltf]          ; Never Happens
;       Movem T2,Elptm0         ; Save Time
;       Log < seconds, >
       Log < : >
       Move  T1,LogJFN
       Move  T2,Bytcnt         ; Byte Count
       Movx  T3,^D10           ; Base 10
       Nout
        Jerr <NOUT failure>
       Log < chars
>
;       Move  T1,LogJFN
;       Fltr  T2,Bytcnt         ; Float Byte Count
;       Fdvr  T2,Elptm0         ; Compute Bytes Per Second
;       Movx  T3,<1b1+Fl%one+Fl%pnt+5b23+3b29>
;       Flout
;        Jerr <FLOUT failure>
;       Log < chars/sec/
;>
       Ret

;Close Net Connection And Reopen It.  Re-Enable For Interrupts
; On Connect Initiate Messages

CLZNET: MOVEI T1,^D4000         ; Give pipe four seconds to empty
       DISMS                   ;  ..
       MOVE T1,NETJFN          ; normal close
       CLOSF
       IFJER.
         CALL DTSTMP           ; We should complain about these
         LOG <%Close error for net link: >
         MOVE T1,LOGJFN
         HRLOI T2,.FHSLF
         ERSTR
          ERJMP .+1
          ERJMP .+1
         MOVE T1,NETJFN
         TXO T1,CZ%ABT         ; Try real hard to close it
         CLOSF                 ;  so we don't eat all job 0 JFNs
          ERJMP .+1
         MOVE T1,NETJFN
         RLJFN
          ERJMP .+1
       ENDIF.
       CALL OPNLSN             ; open connection again
       RET                     ; return

;Open The Net Connection And Listen For Connect Initiates

Opnlsn: Movx  T1,Gj%sht
       Hrroi T2,[Asciz /Srv:27/]        ; Magic Number For Vax Mail Server
       GtJFN
        Jerr <Can't get net JFN for server>
       Movx  T2,Of%rd!Of%wr!<100000,,0>
       Openf
        Jerr <Can't open net JFN>
       Movem T1,NetJFN
       Movx  T2,.moacn         ; Enable For Psi On Network Transitions
       Movx  T3,0b8+<.mocia>B17+<.mocia>B26 ; Channel Zero
       Mtopr
        Jerr <Can't enable for PSI on network transitions>
       Movx  T1,.fhslf
       Movx  T2,1b0            ; Activate Channel Zero
       Aic
       Ret


;Log Name Of Foreign Host

T4nhst: Setzm Hstnam            ; Zero This String
       Setzm 1+Hstnam          ;  ..
       Move  T1,NetJFN         ; Get Net JFN
       Movx  T2,.morhn         ; Return Host Name
       Hrroi T3,Hstnam         ; Where To Put It
       Mtopr
       IFJER.
         Hrroi T1,[Asciz /???/]
         Callret  Logmsg       ; Log confusion
       ENDIF.
       Hrroi T1,Hstnam         ; Copy Name To Log File
       Skipe Dbugsw
        Psout
       Hrroi T1,Hstnam         ; Copy Name To Log File
       Call  Logmsg            ;  ..
       Ret

;Set Up To Time Out If Network Too Slow

Timeit: Move  T1,[.fhslf,,.timel]
       Move  T2,[Timen]                ; Milliseconds To Allow
       Movei T3,1              ; Channel One
       Timer
        Jerr <Can't time myself>
       Movx  T1,.fhslf         ; Activate Timer Channel
       Movx  T2,<1b1>
       Aic
       Ret

;Cancel Above Timer Request

Cncltm: Move  T1,[.fhslf,,.timal] ; Remove All Pending Timer Requests
       Movei T3,1              ; For This Channel
       Timer
        Jerr <Can't remove pending timer request>
       Ret


;Here On Timeout

Timout:
;       Call Dtstmp
       Skipn KEPLIV            ; Skip if still alive
       Die  <Timeout Occured>  ; [153] No activity, dead
       Setzm KEPLIV            ; Clear keep alive flag
       Push  P,T1              ; Save ACs before calling Timeit
       Push  P,T2
       Push  P,T3
       Call  Timeit            ; Start new timer
       Pop   P,T3              ; Restore ACs before resuming
       Pop   P,T2
       Pop   P,T1
       Debrk                   ; Allow things to continue

;Here If Net Link Dies While Outputting To It

Dmplnk: Cis                     ; Zap Things
       Movx  T1,Cz%abt         ; Abort The Net JFN
       Hrr   T1,NetJFN         ;  ..
       Closf                   ;  ..
        Erjmp .+1              ; Don'T Care
       Call  Dtstmp
       Log <----Connection aborted

>
       Movx  T1,.fhslf         ; Deactivate Connect Initiate Channel
       Movx  T2,<1b0>          ;  ..
       Dic                     ;  ..
       Call  Cncltm            ; Cancel Pending Timer Requests
       Move  T1,LogJFN         ; Close Log File
       Closf
        Erjmp .+1
;       Jrst  VMAIL0            ; Go Wait For New Mail
       Jrst  VMAIL             ; Restart on connection abort


;Here On Fatal Wipeout (Jsys Which Can'T Fail Does, For Instance)

Fatal:  Movx  T1,.fhslf
       Dir                     ; Disbale Interrupts
       Cis                     ; Clear Interrupts
; Remove out-of-synch message on fatal error
;;      Move  T1,NetJFN         ; Type A Record To Force Net Buffers Out
;;      Hrroi T2,[Asciz /
;;?VMAIL Internal Error/]
;;      Setzb T3,T4             ; Add Question Mark So Mail Isn'T Requeued
;;      Soutr                   ;  ..
;;       Erjmp .+1
;;      Movei T1,^D5000         ; Wait Five Seconds
;;      Disms
       skipe t1,logJFN
        Closf
         Erjmp .+1
       setzm logJFN
       Movx  T1,.fhslf!cz%abt  ; Abort All JFNs
       Clzff                   ;  ..
        ERJMP .+1              ; Ignore errors in the fatal error routine
       Call  Opnlog            ; Reopen Log File
       Movem T1,LogJFN
       Call  Dtstmp
       Log <Error restart...
>
       Tmsg <VMAIL error restart...
>
       Move T1,LogJFN
       CLOSF%
        ERJMP .+1
       Movei T1,^D5000         ; Wait Some More
       Disms
       Jrst VMAIL              ; And Fire Up The World Again

;Disable Capabilities So Quota-Checking Happens

Capoff: Push  P,T1              ; Don'T Clobber
       Movx  T1,.fhslf         ; Get My Caps
       Rpcap
       Movem T3,Capenb         ; Remember For Later
       Setz  T3,               ; No Caps At All
       Epcap
       Pop   P,T1              ; Restore
       Ret


;Re-Enable Caps

Capon:  Push  P,T1              ; No Clobberage
       Movx  T1,.fhslf
       Move  T3,Capenb         ; Caps We Had Before
       Epcap
       Pop   P,T1
       Ret

CRLF0:  ASCIZ /
/

; FROM DMASER.MAC
A==1
B==2
C==3

WAKEUP: SKIPE T2,MYPID          ; have a PID already?
        TDZA T1,T1             ; yes, use it
         MOVX T1,IP%CPD        ; no, create a PID
       MOVEM T1,IPCBLK+.IPCFL
       MOVEM T2,IPCBLK+.IPCFS  ; PID to use if one there
       SETZM IPCBLK+.IPCFR     ; send to INFO
       MOVX T1,<.IPCI2+3,,BUFFER> ; length of INFO msg,,where INFO msg is
       MOVEM T1,IPCBLK+.IPCFP
       MOVX T1,.IPCIW          ; return PID associated with name
       MOVEM T1,BUFFER+.IPCI0
       SETZM BUFFER+.IPCI1     ; duplicate copy not needed
       DMOVE T1,[ASCII/[SYSTEM]MM/] ; 1st part of PID to look up
       DMOVEM T1,BUFFER+.IPCI2
       MOVE T1,[ASCII/AILR/]   ; 2nd part of PID to look up
       MOVEM T1,BUFFER+.IPCI2+2
       MOVX T1,.IPCFP+1                ; length of block
       MOVEI T2,IPCBLK         ; get MMailr's PID
       MSEND%
        ERJMP R                ; looks like INFO isn't there
       MOVE T1,IPCBLK+.IPCFS   ; get the PID I made
       MOVEM T1,MYPID          ; remember it for next time
       DO.
         SETZM IPCBLK+.IPCFL   ; no flags
         SETZM IPCBLK+.IPCFS   ; any sender
         MOVE T1,MYPID         ; I'm the receiver
         MOVEM T1,IPCBLK+.IPCFR
         MOVX T1,<10,,BUFFER>  ; place to put the reply
         MOVEM T1,IPCBLK+.IPCFP
         MOVX T1,.IPCFP+1      ; length of block
         MOVEI T2,IPCBLK       ; get reply from INFO
         MRECV%
          ERJMP R              ; failure irrelevant here
         LOAD T1,IP%CFC,IPCBLK+.IPCFL ; see who sent message
         CAIE T1,.IPCCC                ; from <SYSTEM>IPCF?
          CAIN T1,.IPCCF       ; no, from <SYSTEM>INFO?
          IFSKP.
            LOOP.              ; no, get another message
          ENDIF.
       ENDDO.
       JN <IP%CFE,IP%CFM>,IPCBLK+.IPCFL,R ; give up if undeliverable
       SETZM IPCBLK+.IPCFL     ; no flags
       MOVE T1,MYPID           ; I'm the sender
       MOVEM T1,IPCBLK+.IPCFS
       MOVE T1,BUFFER+.IPCI1   ; MMailr is the recipient
       MOVEM T1,IPCBLK+.IPCFR
       MOVX T1,<1,,BUFFER>     ; one word from BUFFER
       MOVEM T1,IPCBLK+.IPCFP
       MOVX T1,'PICKUP'                ; magic word to wake up MMailr
       MOVEM T1,BUFFER
       MOVX C,^D20
       DO.
         MOVX T1,.IPCFP+1      ; length
         MOVEI T2,IPCBLK       ; send wakeup to MMailr
         MSEND%
         IFJER.
           MOVEI T1,^D1000     ; failed, wait a bit
           DISMS%
           SOJG C,TOP.         ; try a few times
           RET                 ; failed, give up
         ENDIF.
       ENDDO.
       MOVX T1,.MUQRY          ; query function for MUTIL%
       MOVEM T1,BUFFER
       MOVE T1,MYPID           ; query packets for our PID
       MOVEM T1,BUFFER+1
       MOVX C,^D20             ; number of retries
       DO.
         MOVX T1,.IPCFP+2      ; number of words to return
         MOVEI T2,BUFFER       ; argument block in BUFFER
         MUTIL%
         IFJER.
           MOVEI T1,^D1000     ; wait a bit
           DISMS%
           SOJG C,TOP.         ; retry a few times
           RET
         ENDIF.
       ENDDO.
       DO.
         SETZM IPCBLK+.IPCFL   ; no flags
         SETZM IPCBLK+.IPCFS   ; sender is filled in by monitor
         MOVE T1,MYPID         ; I'm the receiver
         MOVEM T1,IPCBLK+.IPCFR
         MOVX T1,<10,,BUFFER>  ; where MMailr reply will go
         MOVEM T1,IPCBLK+.IPCFP
         MOVX T1,.IPCFP+1      ; size of block
         MOVEI T2,IPCBLK       ; get reply from MMailr
         MRECV%
          ERJMP .+1            ; error uninteresting here
         LOAD T1,IP%CFC,IPCBLK+.IPCFP ; get sender code
         IFN. T1               ; special sender?
           CAIE T2,.IPCCF      ; from <SYSTEM>INFO
            CAIN T2,.IPCCP     ; or private <SYSTEM>INFO?
             LOOP.             ; yes, try for another message
         ENDIF.
       ENDDO.
       RET

       End VMAIL