Google
 

Trailing-Edge - PDP-10 Archives - bb-bt99s-bb - 10,7/decmai/mx/mxlcl.b36
There are 19 other files named mxlcl.b36 in the archive. Click here to see a list.
MODULE mxlcl =
BEGIN
!	COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1985, 1989.
!	ALL RIGHTS RESERVED.
!
!	THIS SOFTWARE IS FURNISHED UNDER A  LICENSE AND MAY BE USED AND  COPIED
!	ONLY IN  ACCORDANCE  WITH  THE  TERMS OF  SUCH  LICENSE  AND  WITH  THE
!	INCLUSION OF THE ABOVE  COPYRIGHT NOTICE.  THIS  SOFTWARE OR ANY  OTHER
!	COPIES THEREOF MAY NOT BE PROVIDED  OR OTHERWISE MADE AVAILABLE TO  ANY
!	OTHER PERSON.  NO  TITLE TO  AND OWNERSHIP  OF THE  SOFTWARE IS  HEREBY
!	TRANSFERRED.
!
!	THE INFORMATION IN THIS  SOFTWARE IS SUBJECT  TO CHANGE WITHOUT  NOTICE
!	AND SHOULD  NOT  BE CONSTRUED  AS  A COMMITMENT  BY  DIGITAL  EQUIPMENT
!	CORPORATION.
!
!	DIGITAL ASSUMES NO  RESPONSIBILITY FOR  THE USE OR  RELIABILITY OF  ITS
!	SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!++
! FACILITY:	Decmail/MS Message eXchange (MX) Local Mail Subtasks
!
! ABSTRACT:	This module contains the data structures and routines used by
!          the Local Mail Sub-System.
!
! ENVIRONMENT:	Tops-10/Tops-20 User Mode
!
! AUTHOR: Tim Grady, CREATION DATE: 18 February 1985
!
! MODIFIED BY:  Richard B. Waddington
!
!   MX: VERSION 1.0
! 01	-
!--
!
! INCLUDE FILES:
!
%IF %SWITCHES(TOPS20) %THEN
    LIBRARY 'monsym';
    UNDECLARE time;
    LIBRARY 'mxjlnk';
%FI
LIBRARY 'mxnlib' ;	! Our version of NML's utility library
LIBRARY 'mxlib';		! Local mail service symbols
REQUIRE 'blt';
!
! EXTERNAL REFERENCES:
!
EXTERNAL ROUTINE
        mx$file_routines,
        mx$error_routines,
        mx$database_routines,
        mx$message_queue_post,
        mx$message_queue_checkpoint,
        mx$release_message,
        mx$release_asciz,
        nmu$ipcf_manager,
        nmu$page_allocator,
	nmu$text_manager,
	nmu$sched_manager,
	nmu$queue_manager,
	nmu$memory_manager,
	nmu$table_routines,
%IF %SWITCHES(TOPS20) %THEN
        mx$update_pobox_status: NOVALUE,
%FI
        log;

%IF %SWITCHES(TOPS20) %THEN
    declare_jsys(odtim);
%FI

EXTERNAL
        pobsts: VOLATILE,
        nodnam,
        nettab: VECTOR[max_number_of_domains],
        active_message_table,
        uc_tab,
	work_queue:	SQ_HEADER,
        rtrylc;                     !Retry time in minutes

!
! TABLE OF CONTENTS
!
FORWARD ROUTINE
    scan_pkt,
    new_msg,
    old_msg,
    get_file: NOVALUE,
    get_dest: NOVALUE,
    get_sender: NOVALUE,
    get_subj: NOVALUE,
    copy_asciz,
    copy_string,
    find_work_req,
    parse_rcpt,
    pname,
    pnode,
    local_deliver: NOVALUE,
    append_mail,
    kleanup: NOVALUE,
    get_header,
    get_name,
    get_quota: NOVALUE,
    nag: NOVALUE,
    logged_in,
    mx$get_username,
    mx$validate_local_user,
    mx$build_repair_spec;

!
! OWN STORAGE
!
OWN
    flag,
    error_list: VOLATILE REF list_blk;

!
! EQUATED SYMBOLS
!
LITERAL
    user_name_only = 0,
    name_and_quota = 1,
    actjob = %O'56000014';              !For TOPS-20 getab


%global_routine ('LCLSRV') :NOVALUE =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This task listens for connections from User Interface programs and
!   sets up WORK-REQUESTS and ACTIVE-MESSAGE-TABLE entries for MX.  The
!   WORK-REQUESTS get queued to the WORK-QUEUE.
!
! FORMAL PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--

    BEGIN				        !BEGIN LCLSRV
    LITERAL
        page_size = 512;

    MACRO
        pidname =
	%IF %SWITCHES(TOPS10) %THEN
		ch$asciz('MAILER')
	%ELSE					!TOPS-20
            %IF %VARIANT EQL 0 %THEN
                ch$asciz('MXMAIL')
            %ELSE
                ch$asciz('MXTEST')
            %FI  %FI %,

![308] ADD MACRO REBIND_PID_NAME
        rebind_pid_name(id,nam)[] =
            BEGIN
            nmu$sched_sleep(30);
            %IF %SWITCHES(TOPS20) %THEN
                BEGIN
                declare_jsys(msend);

                STACKLOCAL
                    nfoblk: VECTOR[$ipcfp + 1],
                    nforeq:  VECTOR[$ipci2 + 2];

                nfoblk[$ipcfl] = 0;                             !NO FLAGS
                nfoblk[$ipcfs] = nmu$ipcf_map_id(id);           !MY PID
                nfoblk[$ipcfr] = 0;                             !SEND TO INFO
                nfoblk[$ipcfp] = (($ipci2 + 2) ^ 18) + nforeq;  !LEN,,ADDRESS 

                nforeq[$ipci0] = $ipcii;                        !GIVE IT A NAME
                nforeq[$ipci1] = 0;                             !NO DUPS
                CH$MOVE(7,nam,CH$PTR(nforeq[$ipci2]));          !COPY THE NAME

                $$msend($ipcfp+1, nfoblk);
                END
            %FI 
![310] MOVE FOLLOWING END OUT OF TOPS-20 CONDITIONAL
            END % ;

    LOCAL
        ipcf_packet: REF ipcf_msg,	        !address of the ipcf message
        msg: REF message_table_entry,	        !message block
        message_id,			        !MX Message Id
        Sender_caps,			        !Sender's capabilities
        Sender_pid,			        !IPCF PID of sender
        Sender_Uid,			        !Sender's User ID
	Server_Pid,			        !IPCF Process id
	work_request;

    BIND
        slpptr = CH$PTR(UPLIT(%ASCIZ' - Sleeping 10 seconds')),
        name = pidname;

    !
    ! Create the local mailer PID
    !

    INCR i FROM 1 TO 30 DO              ![308] CHANGE FROM 10 TO 30
        BEGIN
        IF (server_pid = NMU$IPCF_CREATE(name, $false)) NEQ 0
        THEN
            EXITLOOP;

%(318)% $error(SEVERITY = STS$K_WARNING,
%(318)%        FACILITY = $err,
               CODE = LS$CCP,
               MESSAGE_DATA = slpptr);

        nmu$sched_sleep(10)
        END;

    IF .server_pid EQL 0
    THEN
%(318)% $error(SEVERITY = STS$K_SEVERE,      !Crash MX - NO PID...
%(318)%        FACILITY = $err,
               CODE = LS$CCP);
    WHILE 1 DO
        BEGIN				        !BEGIN main loop
        ipcf_packet = NMU$IPCF_RECEIVE(
                       .Server_Pid, Sender_Pid, Sender_Uid, Sender_caps);

![308] ADD TEST FOR PACKETS FROM INFO
        IF .sender_pid EQL 0                    ![308]
        THEN                                    ![308]
            REBIND_PID_NAME(.server_pid,name)   ![308]
        ELSE                                    ![308]
            BEGIN                               ![308]

        $TRACE('LCLSRV running');

        msg = scan_pkt(.ipcf_packet, .sender_pid, .sender_uid, .sender_caps);

        nmu$ipcf_transmit(.server_pid, .sender_pid, .ipcf_packet, 512);

            END                                 ![308]
        END;				        !END of main loop

    END;				! END LCLSRV

GLOBAL ROUTINE scan_pkt (packet, pid, uid, cap) =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine scans an incoming IPCF packet, and validates the
!    format and content of that packet.  There are two types of packets:
!    primary packets initiate the posting of a message to MX; secondary
!    packets are used when there is more information needed to complete
!    the posting of a message.  Primary packets must contain the file
!    name of the message to be posted.  Primary packets indicate whether
!    there will be secondary packets for this message by the cell hdr_state
!    which contains $msg_incomplete if there are more packets to come.
!    The last packet in a sequence contains the value $msg_complete in this
!    cell.
!
!    Packets are sequenced, and must arrive in sequence.  If a packet is
!    found to be out of sequence, the message is invalidated, and an error
!    returned to the user agent (usually MS).
!
! FORMAL PARAMETERS:
!
!    packet	- IPCF packet
!    pid	- sender's IPCF PID
!    uid	- sender's user number (TOPS20) or PPN (TOPS10)
!    cap	- sender's enabled capabilities (from IPCF header)
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!       The address of the Active Message Table Entry
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				        !BEGIN scan_pkt
    MAP
        packet: REF ipcf_msg;		!reference the IPCF packet

    BIND
        hdr = packet[ipcf_header]: ipcf_hdr;	!and the header data

    LOCAL
        rootine,
        pck: VOLATILE,
        msg: REF message_table_entry VOLATILE,
        rec: REF ipcf_rec VOLATILE,             !IPCF record
        sts;

    ENABLE
        mx$error_handler (pck,msg,rec);

    $TRACE('SCAN%P called');
    pck = .packet;
    !
    ! If this is a new message, get the file spec and build a message table
    ! entry, otherwise lookup the old message table entry and continue.
    !

    CASE .hdr[hdr_type]
        FROM min_hdr_type TO max_hdr_type OF
        SET
        [lcl_post]:	msg = new_msg(.packet, .pid, .uid, .cap);
        [lcl_cont]:	msg = old_msg(.packet, .pid, .uid, .cap);
        [OUTRANGE]:     $signal_error(
%(318)%                     SEVERITY=STS$K_ERROR,
%(318)%                     FACILITY=$err,
                            CODE=ls$iht);       
        TES;
    !
    ! We now have the message block, so scan the ipcf packet, and add the
    ! contents to the message block
    !

    rec = hdr[hdr_record];		        !get first record
    INCR i FROM 1 TO .hdr[hdr_record_count] DO
        BEGIN				        !BEGIN packet scan
        rootine =
            (CASE .rec[rec_type]
                FROM min_rec_type TO max_rec_type OF
                    SET
                    [rec_dest]:	    get_dest;
                    [rec_sender]:   get_sender;
                    [rec_file]:	    get_file;
                    [rec_subj]:     get_subj;
                    [OUTRANGE]:     BEGIN
                                    $signal_error(
%(318)%                                 SEVERITY=STS$K_SEVERE,
%(318)%                                 FACILITY=$err,
                                        CODE=ls$irt);       
                                    EXITLOOP;
                                    END;
                    TES);

        (.rootine) (CH$PTR(rec[rec_data]), (.rec[rec_length] - 3) * 5, .msg);

        rec = .rec + .rec[rec_length];
        END;				        !END packet scan

    IF .msg NEQ 0
    THEN
        BEGIN
        sts = .msg[msg_state];
        CASE .sts FROM 1 TO 4 OF
            SET
            [$msg_complete]:    mx$message_queue_post(.msg);

            [$msg_incomplete]:  ; !Do nothing
            [$msg_warning]:     IF .hdr[hdr_status] EQL $msg_complete
                                THEN
                                    mx$message_queue_post(.msg);

            [$msg_canceled]:    mx$release_message(.msg);
            TES;
        hdr[hdr_status] = .sts;
        END
    ELSE
        hdr[hdr_status] = $msg_canceled;

    RETURN .msg;
    END;				! END scan_pkt
ROUTINE new_msg (packet, pid, uid, caps) =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine is called whenever an IPCF packet arrives with hdr_type
!    set to lcl_post.  The packet is scanned for the message file spec,
!    and a new message table entry is created for this message.  The
!    sender field is set to the sending UID's user name (which may be
!    overridden later, if the sender is privileged) and the address of
!    the message table entry is returned.
!
! FORMAL PARAMETERS:
!
!    packet	- IPCF packet address
!    pid	- sender's IPCF pid
!    uid	- sender's user number (TOPS20) or PPN (TOPS10)
!    cap	- sender's enabled capabilities, from IPCF header
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALIE
!
!       The address of the Message data block (an Active Table Entry)
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				        !BEGIN new_msg
![307] ADD VUFLG DEFINITION.  REMOVE MSG_ID (IT WASN'T BEING USED...)
    LOCAL
        pck: VOLATILE,
        msg: REF message_table_entry VOLATILE,  !address of msg block
        vuflg;                                ![307] FLAG FOR INVALID SENDERS

    MAP
        packet: REF ipcf_msg;	        !reference the IPCF packet

    BIND
        hdr = packet[ipcf_header]: ipcf_hdr;	!and the header data

    ENABLE
        mx$error_handler(pck, msg);

    pck = .packet;
    msg = mx$get_message_table_entry;

    IF .msg EQL 0
    THEN
        $signal_error(				!It failed...
%(318)%     SEVERITY=STS$K_WARNING,
%(318)%     FACILITY=$err,
            CODE=mx$nom);

    !Now, initialize the message block

    msg[msg_fil_spec] = 0;

![307] CALL MX$GET_USERNAME SO THAT IT TELLS IF SENDER IS AN INVALID USER
    msg[msg_sender_string] = mx$get_username(.uid,vuflg); ![307]

    msg[msg_work_req_list] = 0;
    msg[msg_work_req_count] = 0;
    msg[msg_sender_domain] = .hdr[hdr_domain_id];
    msg[msg_state] = .hdr[hdr_status];
    msg[msg_sender_pid] = .pid;
    msg[msg_sender_uid] = .uid;
    msg[msg_sender_caps] = .caps;
    hdr[hdr_id] = .msg[msg_msg_id];
    $TRACE_ALWAYS(.msg[msg_unique_id], %(318)%
                  'Message submitted by %A',CH$PTR(.msg[msg_sender_string]));

![307] IF VUFLG IS SET, CANCEL THIS MESSAGE
    IF NOT .vuflg
    THEN
        BEGIN
        msg[msg_state] = $msg_canceled;
        $TRACE_ALWAYS(  .msg[msg_unique_id],  %(318)%
                        '...%A is not a valid user - Message rejected',
                        CH$PTR(.msg[msg_sender_string]))
        END;

    RETURN (.msg);			        !return the message table entry

    END;				    !END new_msg
ROUTINE old_msg (packet, pid, uid, caps) =
!++
! FUNCTIONAL DESCRIPTION:
!
!       This routine is called whenever an IPCF packet is recieved with header
!   type lcl_cont.  It fetches the message block from the held message table
!   and performs some consistancy checks.  In particular, the PID and UID must
!   match in the held data and the new packet.
!
! FORMAL PARAMETERS:
!
!	msg_id: The index into the HELD_DATA_TABLE
!       pid:    The pid of the requesting process
!       uid:    The user id of the requestor
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! RETURN VALUE:
!
!       The address of the message data block
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN

    MAP
        packet: REF ipcf_msg;	        !reference the IPCF packet

    BIND
        hdr = packet[ipcf_header]: ipcf_hdr;	!and the header data

    LOCAL
        msg_id,
        pck: VOLATILE,
        msg: REF message_table_entry VOLATILE;

    ENABLE
        mx$error_handler(pck, msg);

    $TRACE('A continuation block...');
    pck = .packet;
    NMU$TABLE_FETCH(active_message_table, .hdr[hdr_id], msg);

    IF .msg EQL 0           
    THEN                        !Invalid continuation packet
        $signal_error(
%(318)%     SEVERITY=STS$K_ERROR,
%(318)%     FACILITY=$err,
            CODE=ls$icp);       

    IF .msg [msg_sender_pid] NEQ .pid
    THEN                        !Invalid Sender Pid
        $signal_error(
%(318)%     SEVERITY=STS$K_ERROR,
%(318)%     FACILITY=$err,
            CODE=ls$isp);

    IF .msg [msg_sender_uid] NEQ .uid
    THEN                        !Invalid Sender User id
        $signal_error(
%(318)%     SEVERITY=STS$K_ERROR,
%(318)%     FACILITY=$err,
            CODE=ls$isu);

    msg[msg_state] = .hdr[hdr_status];
    RETURN (.msg);				! return message block

    END;
ROUTINE get_file (ptr, len, msg): NOVALUE =
    BEGIN
    MAP
        msg: REF message_table_entry;
    LOCAL
        p1;

    IF .msg[msg_fil_spec] NEQ 0
    THEN                    !Duplicate Message File
        $signal_error(
%(318)%     SEVERITY=STS$K_ERROR,
%(318)%     FACILITY=$err,
            CODE=ls$dmf,
            MESSAGE_DATA=(CH$PTR(.msg[msg_fil_spec]), .ptr));

    IF NOT mx$file_exists(.ptr)
    THEN                    !No message file
        BEGIN
        $signal_error(
%(318)%     SEVERITY=STS$K_ERROR,
%(318)%     FACILITY=$err,
            CODE=ls$nmf,
            MESSAGE_DATA=.ptr);
        msg[msg_fil_spec] = 0;
        END
    ELSE
        BEGIN
%if %switches(TOPS20)
%then
!
!TOPS-20 likes to include ;P770000 at the end of the message file spec.
!This is ugly, so we search for the ";", and write a null in its place
!
        p1 = CH$FIND_CH(.len*5,.ptr,%C';');
        IF NOT CH$FAIL(.p1)
        THEN
            CH$WCHAR(0,.p1);
%fi
        msg[msg_fil_spec] = copy_asciz(.ptr);
        END;

    $TRACE('The message file is %A', CH$PTR(.msg[msg_fil_spec]));
    END;

GLOBAL ROUTINE get_dest (ptr, len, msg): NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine adds the destination string pointed to by ptr to the
!    proper work request for the message specified by msg.  The destination
!    node is validated, and the destination string is added to the list
!    of destination strings for the work request for that node.  If no
!    work request exists for the node, a new work request is created.
!
! FORMAL PARAMETERS:
!
!    msg	- message table entry block
!    ptr	- pointer to ASCIZ destination string
!    len	- length of the source string
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!       NONE
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				! BEGIN msg_dest

    MAP
        msg:REF  message_table_entry;

    STACKLOCAL
        pobbuf: VECTOR[CH$ALLOCATION(max_string_length)];

    LOCAL
        list:   REF list_blk,
        domain,
        rcpt_str,
        node_str,
        name_len,
        sys_flg,
        err_flg,
        rcpt_data_blk: REF rb_block,
        work_request: REF work_request_block;

    err_flg = sys_flg = 0;
    rcpt_str = copy_asciz(.ptr);
    rcpt_data_blk = parse_rcpt(CH$PTR(.rcpt_str));

    IF .rcpt_data_blk[rb_node_ptr] EQL 0        ![314]If there's no nodename
    THEN                                        ![314] then assume the local
        BEGIN                                   ![314] domain.
        domain = $local;                        ![314]
        node_str = 0;                           ![314]
        END                                     ![314]
    ELSE                                        ![314]
        BEGIN
        node_str =
            nmu$memory_get(CH$ALLOCATION(.rcpt_data_blk[rb_node_len]+4, 8));
        ptr = CH$PTR(.node_str,0,8);
        CH$WCHAR_A(0,ptr);
        CH$WCHAR_A(0,ptr);
        CH$WCHAR_A(.rcpt_data_blk[rb_node_len], ptr);
        ptr = CH$MOVE(.rcpt_data_blk[rb_node_len],
                  .rcpt_data_blk[rb_node_ptr],
                  .ptr);
        CH$WCHAR(0,.ptr);

        domain = mx$data_validate(.node_str,
                                  .msg[msg_sender_domain],
                                  -1);

        domain = .domain<0,18,1>;
        END;

    IF .domain GEQ 0
    THEN
        BEGIN                                       ![314]If there's no network
        IF .node_str EQL 0                          ![314] then there's only
        THEN                                        ![314] one work request
            work_request = .msg[msg_work_req_list]  ![314]
        ELSE                                        ![314]
            work_request = find_work_req( .msg[msg_work_req_list],
                                          CH$PTR(.node_str,3,8), 
                                          .rcpt_data_blk[rb_node_len]);

        IF .domain EQL $local
        THEN
            BEGIN
            IF NOT (sys_flg=mx$validate_local_user(.rcpt_data_blk[rb_name_len],
                                                   .rcpt_data_blk[rb_name_ptr],
                                                   pobbuf))
            THEN
                BEGIN
                err_flg = 1;
                $signal_error(
%(318)%             SEVERITY=STS$K_WARNING,
%(318)%             FACILITY=$err,
                    CODE=mg$nsu,
                    MESSAGE_DATA=(CH$PTR(UPLIT(%ASCIZ'LOCAL')),
                                    CH$PTR(pobbuf)));
                END     
                                                %IF %SWITCHES(TOPS10) %THEN
            ELSE                                    !Tops-10 Save the profile
                rcpt_data_blk[rb_profile] = .pobbuf   %FI ;

            IF .sys_flg LSS 0
            THEN
                IF (.msg[msg_sender_domain] NEQ $local) OR
                %IF %SWITCHES(TOPS20) %THEN

                ((.msg[msg_sender_caps] AND (sc_whl OR sc_opr)) EQL 0)

                %ELSE

                (((.msg[msg_sender_caps] AND ip$jac) EQL 0) AND
                	(.msg[msg_sender_uid] NEQ 1^18 + 2))
                %FI
                THEN
                    BEGIN
                    err_flg = $true;
                    $signal_error(
%(318)%                 SEVERITY=STS$K_WARNING,
%(318)%                 FACILITY=$err,
                        CODE=mg$ips);
                    END;

            END;
        END
    ELSE
        BEGIN
        $signal_error(              !Invalid Node Name
            ID=.msg[msg_unique_id],
            SEVERITY=STS$K_WARNING,
            FACILITY=$err,
            CODE=ls$inn,
            MESSAGE_DATA=CH$PTR(.node_str,3,8));
        err_flg = 1;
        END;

    IF .err_flg
    THEN
        BEGIN
        mx$release_asciz(.node_str);
        mx$release_asciz(.rcpt_str);
        nmu$memory_release(.rcpt_data_blk, rb_block_size);
        END
    ELSE
        BEGIN
        if (.work_request EQL 0)
        THEN
            BEGIN
            work_request = mx$get_work_request;
            work_request[req_message_id] = 0;
            work_request[req_domain_id] = .domain;
            work_request[req_destination_node] = .node_str;
            work_request[req_recipient_list] = 0;
            work_request[req_state] = 0;
            list = mx$get_list_blk;
            list[lst_next] = .msg[msg_work_req_list];
            list[lst_data] = .work_request;
            msg[msg_work_req_list] = .list
            END;

        list = mx$get_list_blk;
        list[lst_data] = .rcpt_str;
        list[lst_next] = .work_request[req_recipient_list];
        list[lst_xtra] = .rcpt_data_blk;
        list[lst_stat] = 0;
        work_request[req_recipient_list] = .list;

        END;
    $TRACE('Recipient %A', CH$PTR(.rcpt_str))
    END;				! END get_dest
%global_routine('PARSE_RCPT', ptr) =
    BEGIN
    LOCAL
        parse_data_blk: REF rb_block,
        len,
        err,
        ep,
        erstr;

    erstr = .ptr;
    err = $false;
    parse_data_blk = mx$get_rb_block;
    IF CH$RCHAR(.ptr) EQL %C'@'
    THEN
        BEGIN
        ptr = ep = CH$PLUS(.ptr,1);
        len = pnode(ep);
        parse_data_blk[rb_node_ptr] = .ptr;
        parse_data_blk[rb_node_len] = .len;
        parse_data_blk[rb_name_len] = parse_data_blk[rb_name_ptr] = 0;

        WHILE CH$RCHAR(.ep) EQL %C',' DO 
            BEGIN
            IF CH$A_RCHAR(ep) NEQ %C'@'
            THEN
                BEGIN
                err = $true;
                EXITLOOP
                END;
            ep = CH$PLUS(.ep,1);
            pnode(ep);
            END;

        IF CH$RCHAR_A(ep) EQL %C':'
        THEN
            BEGIN
            pname(ep);
            IF CH$RCHAR_A(ep) EQL %C'@'
            THEN
                BEGIN
                pnode(ep);
                IF CH$RCHAR(.ep) NEQ 0
                THEN
                    err = $true;
                END
            ELSE
                err = $true
            END
        ELSE
            err = $true;

        IF .err
        THEN
            $signal_error(          !Invalid Destination String
%(318)%         SEVERITY=STS$K_ERROR,
%(318)%         FACILITY=$err,
                CODE=ls$ids,
                MESSAGE_DATA=.erstr);
        END
    ELSE
        BEGIN
        ep = .ptr;
        len = pname(ep);
        IF CH$RCHAR(.ptr) EQL %C'"'
        THEN
            BEGIN   !Ignore the quotes
            parse_data_blk[rb_name_ptr] = CH$PLUS(.ptr,1);
            parse_data_blk[rb_name_len] = .len - 2;
            END
        ELSE
            BEGIN
            parse_data_blk[rb_name_ptr] = .ptr;
            parse_data_blk[rb_name_len] = .len;
            END;

        SELECTONE CH$RCHAR(.ep) OF
            SET
            [0]:            BEGIN
                            IF .nodnam EQL 0        ![315]If no network then
                            THEN                    ![314] clear parse data
                                BEGIN                           ![314]
                                parse_data_blk[rb_node_ptr] = 0;![314]
                                parse_data_blk[rb_node_len] = 0;![314]
                                END                             ![314]
                            ELSE                                ![314]
                                BEGIN                           
                                parse_data_blk[rb_node_ptr] = CH$PTR(nodnam);
                                parse_data_blk[rb_node_len] =
                                            CH$LEN(CH$PTR(nodnam));
                                END
                            END;
            [%C' ',%C'@']:  BEGIN
                            parse_data_blk[rb_node_ptr] = ep = CH$PLUS(.ep,1);
                            parse_data_blk[rb_node_len] = pnode(ep);
                            END;
            TES;

        IF CH$RCHAR(.ep) NEQ 0
        THEN
            $signal_error(          !Invalid Destination String
%(318)%         SEVERITY=STS$K_ERROR,
%(318)%         FACILITY=$err,
                CODE=ls$ids,
                MESSAGE_DATA=.erstr);
        END;

    RETURN .parse_data_blk
    END;

GLOBAL ROUTINE pnode(ptr_) =
    BEGIN
    BIND
        ptr = .ptr_;

    LOCAL
        len;

    len = 0;
    WHILE $true DO
        SELECTONE CH$RCHAR(.ptr) OF
            SET
            [%C',', %C':', 0]:  EXITLOOP;

            [OTHERWISE]:        BEGIN
                                ptr = CH$PLUS(.ptr, 1);
                                len = .len + 1;
                                END;
            TES;

    RETURN .len
    END;

GLOBAL ROUTINE pname(ptr_) =
    BEGIN
    BIND
        ptr = .ptr_,
        atptr = CH$PTR(UPLIT(%ASCIZ' at '));

    LOCAL
        erstr,
        qstr,
        len;

    erstr = .ptr;
    len = 0;
    qstr = 0;

    IF CH$RCHAR(.ptr) EQL %C'"'
    THEN
        BEGIN
        qstr = $true;
        len = 1;
        ptr = CH$PLUS(.ptr,1)
        END;

    WHILE $true DO
        BEGIN
        SELECTONE CH$RCHAR(.ptr) OF
            SET
            [%C'"']:        IF .qstr
                            THEN
                                qstr = 0
                            ELSE
                                $signal_error(      !Invalid Destination String
%(318)%                             SEVERITY=STS$K_ERROR,
%(318)%                             FACILITY=$err,
                                    CODE=ls$ids,
                                    MESSAGE_DATA=.erstr);

            [%C'@']:        IF NOT .qstr            !If not inside quotes, exit
                            THEN
                                EXITLOOP;

            [%C' ']:        IF NOT .qstr
                            THEN
                                IF CH$EQL(4,.ptr,4,atptr)
                                THEN
                                    BEGIN
                                    ptr = CH$PLUS(.ptr,3);
                                    EXITLOOP
                                    END;
!                                ELSE
!                                    $signal_error( !Invalid Destination String
!                                        SEVERITY=STS$K_ERROR,
!                                        FACILITY=$err,
!                                        CODE=ls$ids,
!                                        MESSAGE_DATA=.erstr);

            [0]:            EXITLOOP;
            TES;

        ptr = CH$PLUS(.ptr, 1);
        len = .len + 1;
        END;

    RETURN .len
    END;

GLOBAL ROUTINE copy_asciz (ptr) =
    BEGIN
    LOCAL
        p,
        len,
        adr;

    p = .ptr;
    len = 0;
    WHILE CH$RCHAR_A(p) NEQ 0 DO len = .len + 1;
    adr = nmu$memory_get(CH$ALLOCATION(.len+1));
    CH$MOVE(.len, .ptr, CH$PTR(.adr));
    RETURN .adr
    END;

GLOBAL ROUTINE copy_string (ptr, len) =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine copies the specified string into a string block, and
!    returns a pointer to that string block.  The source string is assumed
!    to be an ASCIZ string (terminated by a null).  The destination string
!    block is allocated based on the length of the source, including the null.
!
! FORMAL PARAMETERS:
!
!    ptr	- pointer to source string
!    len	- length of the source string
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!    address of string block
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				! BEGIN copy_string

    LOCAL
      block,				! place to keep string block
      dptr;				! pointer into string block

    block = NMU$MEMORY_GET(CH$ALLOCATION(.len));	! get a block
    dptr = CH$PTR(.block);		! create a pointer to the block
    CH$MOVE(.len, .ptr, .dptr);		! move the string
    RETURN (.block);			! return pointer to string block

    END;				! END copy_string
routine find_work_req (list, ptr, len) =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine will return the address of the work request that coresponds
!    to the node name pointed to by ptr.  The list of work requests is scanned
!    until the work request for the specified node name is found.  
!
! FORMAL PARAMETERS:
!
!    list	- address of first list block in the chain
!    ptr	- pointer to node name
!    len	- length of the node name
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!    address of work request, or 0.
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				! BEGIN find_work_req
    STACKLOCAL
        nodb1: VECTOR[CH$ALLOCATION(max_string_length)],
        nodb2: VECTOR[CH$ALLOCATION(max_string_length)];

    LOCAL
        list_ptr: REF list_blk,			!save list block address here
        req,				        !work request address
        wnode_len;			        !length of work-req node string

    CH$TRANSLATE(uc_tab,                        !Convert node to upper case
                 .len, .ptr,
                 0,
                 .len+1, CH$PTR(nodb1));

    req = 0;				        !assume no match
    list_ptr = .list;			        !copy list pointer
    WHILE .list_ptr NEQ 0 AND .req EQL 0 DO
        BEGIN				        ! BEGIN search loop
        BIND
            curr_req = .list_ptr[lst_data]: work_request_block;

        wnode_len = CH$RCHAR(CH$PTR(.curr_req[ req_destination_node ],2,8));

        CH$TRANSLATE(uc_tab,
                     .wnode_len, CH$PTR(.curr_req[ req_destination_node ],3,8),
                     0,
                     .wnode_len+1, CH$PTR(nodb2));

        IF CH$EQL(.wnode_len,  CH$PTR(nodb2),
                  .len,  CH$PTR(nodb1), 0)
        THEN
            req = curr_req;

        list_ptr = .list_ptr [ lst_next ];
        END;		        		! END of search loop

    RETURN (.req);		        	! return the work request

    END;        				! END find_work_req
ROUTINE get_sender (ptr, len, msg): NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine processes an IPCF sender record.  If the originator has
!    privileges, the sender string is copied into a string block and the
!    existing sender string is superceded with this new string.
!
! FORMAL PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				! BEGIN msg_sender

    MAP
        msg:REF message_table_entry;

    $TRACE('Routine GET_SENDER called');

    %IF %SWITCHES(TOPS20) %THEN

    IF (.msg[msg_sender_caps] AND (SC_WHL OR SC_OPR)) GTR 0

    %ELSE

    IF ((.msg[msg_sender_caps] AND IP$JAC) NEQ 0) OR
	(.msg[msg_sender_uid] EQL 1^18 + 2)

    %FI
    THEN
        BEGIN
        $TRACE('Sender %A being changed to %A',
                CH$PTR(.msg[msg_sender_string]),.ptr);

        IF .msg[ msg_sender_string ] NEQ 0
        THEN
            mx$release_asciz(.msg[ msg_sender_string ]);

        msg[ msg_sender_string ] = copy_asciz(.ptr);
        END;
    END;				! END msg_sender
ROUTINE get_subj (ptr, len, msg): NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!    This routine processes an IPCF sender record.  If the originator has
!    privileges, the sender string is copied into a string block and the
!    existing sender string is superceded with this new string.
!
! FORMAL PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!
! SIDE EFFECTS:
!
!	NONE
!--
    BEGIN				! BEGIN get_subject

    MAP
        msg:REF message_table_entry;

    $TRACE('SUBJECT: %A',.ptr);

    msg[msg_subject_string] = copy_asciz(.ptr);
    END;				! END get_subject


%global_routine ('LCLSPL'):NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!	This task takes WORK-REQUESTS from its input queue, and transmits the
!   message to the appropriate users...
!
! FORMAL PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!    	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!--

    BEGIN
    BIND
	domain = .nettab[0]: domain_data_block;

    LOCAL
        state,
        sleep_time,
        did,
        msg: REF message_table_entry,
	entry: REF work_request_block;

    WHILE 1 DO
    	BEGIN
	entry = nmu$squeue_remove(.domain[dom_spooler_queue]);
	$TRACE('LCLSPL running...');
        nmu$table_fetch(active_message_table, .entry[req_message_id], msg);

        IF .nodnam EQL 0            ![314]Init "domain id" in case of no net
        THEN                        ![314]
            did = 0                 ![314]
        ELSE                        ![314]
            did = mx$data_validate(.entry[req_destination_node],
                                      .msg[msg_sender_domain],
                                      .entry[req_domain_id]);

        IF NOT .did<0,18,1> GEQ 0
        THEN
            BEGIN                   !Invalid Node Name
            $error(
%(318)%         SEVERITY=STS$K_SEVERE,
%(318)%         FACILITY=$err,
                CODE=MG$NNK,
                MESSAGE_DATA=(CH$PTR(UPLIT(%ASCIZ'LOCAL')), 
                              CH$PTR(.entry[req_destination_node], 3, 8)),
                ID=.msg[msg_unique_id]);
            $mx$change_state(.entry, $reject);
            END
        ELSE
            BEGIN
            sleep_time = 0;
            local_deliver(.entry, .msg);
            IF .entry[req_state] EQL $defer
            THEN
                time_current(RTRYLC*60, sleep_time);

            $mx$change_state(.entry,,.sleep_time);
	    END;
        mx$message_queue_checkpoint(.msg);
        END;
    nmu$sched_deschedule();
    END;			!End of LCLSPL
%routine('LOCAL_DELIVER', entry: REF work_request_block,
                          msg: REF message_table_entry): NOVALUE =
    BEGIN
    LOCAL
        head: list_blk,
        plist: REF list_blk,
        tlist: REF list_blk,
        list: REF list_blk,
	state,
        temp;

    $TRACE('Routine LOCAL_DELIVER called');

    plist = head;
    list = head[lst_next] = .entry[req_recipient_list];
    WHILE .list NEQ 0 DO
        BEGIN
        nmu$sched_pause();
        state = append_mail(.list, .msg);
        IF (.state EQL $done) OR (.state EQL $nofile)
        THEN
            BEGIN
            IF .state EQL $done
            THEN
                temp = CH$PTR(UPLIT(%ASCIZ ' '))
            ELSE
                BEGIN
                temp = CH$PTR(UPLIT(%ASCIZ ' NOT '));
                state = $done;
                END;

            $TRACE_ALWAYS(.msg[msg_unique_id],
                'Mail%ADelivered to %A',
                .temp,
                CH$PTR(.list[lst_data])) ;

            mx$release_asciz(.list[lst_data]);
            nmu$memory_release(.list[lst_xtra],rb_block_size);
            tlist = plist[lst_next] = .list[lst_next];
            nmu$memory_release(.list,list_block_size);
            list = .tlist;
            END
        ELSE
            BEGIN
            plist = .list;
            list = .list[lst_next];
            END;

        entry[REQ_RECIPIENT_LIST] = .head[lst_next];
        entry[REQ_STATE] = MAX(.entry[REQ_STATE], .state);
        END;
    END;
%global_routine('APPEND_MAIL', list: REF list_blk, msg: REF message_table_entry) =
    BEGIN
    STACKLOCAL
        dirbuf: VECTOR[CH$ALLOCATION(max_string_length)],
        nambuf: VECTOR[CH$ALLOCATION(max_string_length)],
        iobuf: VECTOR[CH$ALLOCATION(max_string_length)],
        cleanup_vector: VECTOR[3];

    BIND
        msgfil = cleanup_vector[0],
        mailfil = cleanup_vector[1],
        profile = cleanup_vector[2];

    MACRO proppn(pf) =                  !Get the PPN from a user profile (T10)
        BEGIN
        BIND
            _pf = (pf): VECTOR[];

        ._pf[1]             ! .AEPPN == 1
        END %;

    KEYWORDMACRO
        blast_tty(text,user=) =
        BEGIN
        %IF %ISSTRING(%REMOVE(text)) %THEN
            BIND
                tbuf = UPLIT(%ASCIZ %REMOVE(text)),
                pt = CH$PTR(tbuf);
        %ELSE
            STACKLOCAL
                tbuf: VECTOR[CH$ALLOCATION(132)];
            BIND
                pt = CH$PTR(tbuf);
            LOCAL
                tp;

            tp = CH$PTR(tbuf);
            $nmu$text(tp,132,%REMOVE(text));
        %FI

        %IF %NULL(user) %THEN
            nag(-1,pt)
        %ELSE
            %IF %SWITCHES(TOPS20) %THEN
                nag(user,pt)
            %ELSE
                nag(proppn(.profile),pt) 
                                !NOTE:  profile must be set up before this 
            %FI                 !macro is invoked! The value of user is irrelev
        %FI 
        END
        %;

    LOCAL
        msg_size,
	byte_count,
	written,
        linlen,
        error,
        len,
        state,
        used,
        qta,
        pages_left,
        jobnum,
        ptr,
        pqta,
        wqta,
        sys,
        rcpt_data_blk: REF rb_block;

    profile = mailfil = msgfil = 0;
    state = $done;
    rcpt_data_blk = .list[lst_xtra];

    CH$WCHAR(0,CH$MOVE(.rcpt_data_blk[rb_name_len],
                       .rcpt_data_blk[rb_name_ptr],
                       CH$PTR(nambuf)));

    %IF %SWITCHES(TOPS20) %THEN
    IF (sys = mx$validate_local_user(.rcpt_data_blk[rb_name_len],
                                     .rcpt_data_blk[rb_name_ptr], 
                                     dirbuf))
    THEN
        BEGIN
!
! If mx$validate_local_user returned 3, then one or more POBOX: structures
! were dismounted, and we couldn't validate the user.  Try again later
!
        IF .sys EQL 3
        THEN
            RETURN $defer;
!
! We were able to validate the recipient, but we may not be able to find
! the message file in the sender's directory.  We'll look for it, but if we
! don't find it, we'll check POBSTS.  If POBSTS indicates dismounted
! structures, then we'll defer this one.
!
        IF NOT (msgfil = mx$file_open(
                            CH$PTR(.msg[msg_fil_spec]),
                            file_access_read_only,
                            error)) GTR 0
        THEN
            BEGIN
            sys = $nofile;
            IF .msg[msg_sender_domain] NEQ $local
            THEN
                sys = $defer
            ELSE
                BEGIN
                IF .pobsts EQL ok_pobox
                THEN
                    MX$UPDATE_POBOX_STATUS();

                IF .pobsts NEQ ok_pobox
                THEN
                    sys = $defer;
                END;

            $error(
                SEVERITY=STS$K_INFO,
                FACILITY=$err,
                CODE=uf$fof,
                ID=.msg[msg_unique_id],
                MESSAGE_DATA=CH$PTR(.msg[msg_fil_spec]),
                OPTIONAL_MESSAGE=(FAC=$mon),
                OPTIONAL_DATA=.error);

            kleanup(cleanup_vector);
            RETURN .sys;
            END;
!
! If we get here, then we have both a valid user, and an accessible message
! file.  Check to see if it will fit in the recipients directory.
!
        get_quota(dirbuf, pqta, wqta, used);

        mx$file_size(.msg[msg_fil_spec], msg_size, byte_count);

        IF .byte_count EQL 0
        THEN
            BEGIN
%(318)%     $error( FACILITY = $err,
%(318)%             SEVERITY = STS$K_SEVERE,
                    CODE =      mx$mzb,                 !Message has Zero Bytes
                    MESSAGE_DATA = CH$PTR(.msg[msg_fil_spec]),
                    ID=         .msg[msg_unique_id]);
            kleanup(cleanup_vector);
            RETURN $reject
            END;

        qta = (IF logged_in(CH$PTR(nambuf), jobnum)
               THEN
                   .wqta
               ELSE
                   .pqta); 

        IF .msg_size GEQ .qta
        THEN
            BEGIN
!
! Hmmm... The message is bigger than the user's entire directory.  There's
! nothing the user can do about this without the help of his system
! manager.  Reject this message.
!
%(318)%     $error( FACILITY = $err,
%(318)%             SEVERITY = STS$K_SEVERE,
                    CODE =      mx$noq,
                    MESSAGE_DATA = CH$PTR(dirbuf),
                    ID=         .msg[msg_unique_id]);
            kleanup(cleanup_vector);
            RETURN $reject
            END;
!
! Now see if it will fit in the AVAILABLE space.
!
        pages_left = .qta - .used;

        IF .pages_left GTR .msg_size + 1
        THEN
            BEGIN
            $TRACE('User not over quota');

            linlen = get_header(CH$PTR(iobuf),
                                .byte_count,
                                CH$PTR(.msg[msg_sender_string]));

            get_name(dirbuf);

            IF NOT (mailfil = mx$file_open(
                                CH$PTR(dirbuf),
                                file_access_append_only,
                                error)) GTR 0
            THEN
                BEGIN
                $error(FACILITY=$err,
                       SEVERITY=STS$K_WARNING,
                       CODE=uf$fof,
                       MESSAGE_DATA=CH$PTR(dirbuf),
                       OPTIONAL_MESSAGE=(FAC=$mon),
                       OPTIONAL_DATA=.error,
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $defer;
                END;

            if not mx$file_write(.mailfil, CH$PTR(iobuf), .linlen, error)
            then
                BEGIN
%(318)%         $error(SEVERITY =          STS$K_WARNING,
                       CODE =              uf$fwf,
%(318)%                FACILITY =          $err,
                       MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%                optional_message =  (FAC=$mon),
                       optional_data =     .error,
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $defer;
                END;

	    written = 0;
            WHILE (
                linlen = mx$file_read(.msgfil,
                                      CH$PTR(iobuf),
                                      max_string_length,
                                      error)) GTR 0 DO
		BEGIN
                IF NOT mx$file_write(.mailfil, CH$PTR(iobuf), .linlen, error)
                THEN
                    BEGIN
%(318)%             $error(SEVERITY =          STS$K_WARNING,
                           CODE =              uf$fwf,
%(318)%                    FACILITY =          $err,
                           MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%                    optional_message =  (FAC=$mon),
                           optional_data =     .error,
                           ID=.msg[msg_unique_id]);

                    kleanup(cleanup_vector);
                    RETURN $defer;
                    END;

		written = .written + .linlen;
		END;

            IF .linlen NEQ 0
            THEN
                BEGIN
%(318)%         $error(FACILITY =          $err,
%(318)%                SEVERITY =          STS$K_SEVERE,
                       CODE =              uf$frf,
                       MESSAGE_DATA =      CH$PTR(.msg[msg_fil_spec]),
%(318)%                OPTIONAL_MESSAGE =  (FAC=$mon),
                       OPTIONAL_DATA =     .error,
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $reject;
                END;

            IF NOT mx$file_close(.mailfil, file_keep, error)
            THEN
                BEGIN
%(318)%         $error(FACILITY =          $err,
%(318)%                SEVERITY =          STS$K_WARNING,
                       CODE =              uf$fcf,
                       MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%                OPTIONAL_MESSAGE =  (FAC=$mon),
                       OPTIONAL_DATA =     .error,
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $defer;
                END;

            mx$file_set_writer(CH$PTR(dirbuf),CH$PTR(.msg[msg_sender_string]));
            mx$file_close(.msgfil, file_abort, error);

            IF .sys EQL -1
            THEN
                blast_tty(TEXT = ('New SYSTEM mail available'))
            ELSE
                blast_tty(USER = CH$PTR(nambuf),
                          TEXT = ('You have mail from %A',
                                  CH$PTR(.msg[msg_sender_string])) );
            END
        ELSE                                ! Over quota
            BEGIN
![319] Make sure we release any open files here.
            kleanup(cleanup_vector);

            IF NOT .list[lst_quota]
            THEN
                BEGIN
                $TRACE_ALWAYS(.msg[msg_unique_id],
                     %STRING('%A is overquota (%D/%D) (p/w)%/',
                     '                          %D pages in use.'),
                     CH$PTR(.list[lst_data]),.pqta,.wqta,.used);

                list[lst_quota] = 1;
                END;

            blast_tty(USER = CH$PTR(nambuf),
                      TEXT = (%STRING('You have undeliverable mail from %A.',
                                    '%/  The mail is %D pages long and it ',
                                    'would put you over quota.'),
                              CH$PTR(.msg[msg_sender_string]),
                              .msg_size) );
            state = $defer;
            END;
        END
    ELSE
        BEGIN
%(318)% $error(SEVERITY=STS$K_SEVERE,
%(318)%        FACILITY=$err,
               CODE=mg$nsu,
               MESSAGE_DATA=(CH$PTR(UPLIT(%ASCIZ'LOCAL')),
                             CH$PTR(.list[lst_data])),
               ID=.msg[msg_unique_id]);
        RETURN $reject
        END;
    %ELSE   !BEGIN TOPS10 CONDITIONAL

    profile = .rcpt_data_blk[rb_profile];
    IF NOT ufdcre(.profile)
    THEN
        BEGIN
%(318)% $error( FACILITY = $err,
%(318)%         SEVERITY = STS$K_WARNING,
                CODE = ufd$cr,
                MESSAGE_DATA = .list[lst_data]);
        kleanup(cleanup_vector);
        RETURN $defer
        END;

    dirbuf = proppn(.profile);
    get_name(dirbuf); ! on TOPS10, dirbuf contains the ppn...
                      !...here it contains "DSK:MAIL.TXT[p,pn]"

    IF NOT (mailfil = mx$file_open(             !Open the MAIL.TXT...
                        CH$PTR(dirbuf),
                        file_access_append_only,!...for append if it exists...
                        error)) GTR 0
    THEN
        BEGIN
        SELECTONE .error OF
            SET
            [uf$lok]:                               !File is locked...
                BEGIN                               !...try again later
                kleanup(cleanup_vector);
                RETURN $defer;
                END;
            [OTHERWISE]:
                BEGIN
%(318)%         $error(FACILITY=$err,           !Oh well, rejct the message
%(318)%                SEVERITY=STS$K_SEVERE,
                       CODE=uf$fof,
                       MESSAGE_DATA=CH$PTR(dirbuf),
%(318)%                OPTIONAL_MESSAGE=(FAC=$mon),
                       OPTIONAL_DATA=(.error<0,18,0>,.error<18,18,0>),
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $reject;
                END;
            TES;
        END;

    !Here if MAIL.TXT file has been successfully opened...

    pqta = mx$file_structure(.mailfil);            !Put the SIXBIT structure in
                                                   !pqta for use by GET_QUOTA
    get_quota(proppn(.profile), pqta, wqta, used);    

    IF NOT (error = mx$file_size(.msg[msg_fil_spec], msg_size, byte_count))
    THEN
        BEGIN
        kleanup(cleanup_vector);
        IF .error<left_half> EQL erfnf_
        THEN
            RETURN $done;

        RETURN $defer
        END;

    IF .byte_count EQL 0
    THEN
        BEGIN
%(318)% $error( FACILITY = $err,
%(318)%         SEVERITY = STS$K_SEVERE,
                CODE =      mx$mzb,                     !Message has Zero Bytes
                MESSAGE_DATA = CH$PTR(.msg[msg_fil_spec]),
                ID=         .msg[msg_unique_id]);
        kleanup(cleanup_vector);
        RETURN $reject
        END;

    qta = (IF logged_in(proppn(.rcpt_data_blk[rb_profile]), jobnum)
           THEN
               .wqta
           ELSE
               .pqta);

    IF .msg_size GEQ .qta
    THEN
        BEGIN
        get_name(dirbuf);

%(318)% $error( FACILITY = $err,
%(318)%         SEVERITY = STS$K_SEVERE,
                CODE =      mx$noq,
                MESSAGE_DATA = CH$PTR(dirbuf),
                ID=         .msg[msg_unique_id]);
        kleanup(cleanup_vector);
        RETURN $reject
        END;

    pages_left = .qta - .used;

    IF .pages_left GTR .msg_size + 1
    THEN
        BEGIN
        $TRACE('User not over quota');

        linlen = get_header(CH$PTR(iobuf),
                            .byte_count,
                            CH$PTR(.msg[msg_sender_string]));

        IF NOT (msgfil = mx$file_open(
                            CH$PTR(.msg[msg_fil_spec]),
                            file_access_read_only,
                            error)) GTR 0
        THEN
            BEGIN
            kleanup(cleanup_vector);
            RETURN $done;
            END;

        if not mx$file_write(.mailfil, CH$PTR(iobuf), .linlen, error)
        then
            BEGIN
%(318)%     $error(SEVERITY =          STS$K_SEVERE,
                   CODE =              uf$fwf,
%(318)%            FACILITY =          $err,
                   MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%            optional_message =  (FAC=$mon),
                   optional_data =     .error,
                   ID=.msg[msg_unique_id]);

            kleanup(cleanup_vector);
            RETURN $reject;
            END;

        written = 0;
        WHILE (
            linlen = mx$file_read(.msgfil,
                                  CH$PTR(iobuf),
                                  max_string_length,
                                  error)) GTR 0 DO
            BEGIN
            IF NOT mx$file_write(.mailfil, CH$PTR(iobuf), .linlen, error)
            THEN
                BEGIN
%(318)%         $error(SEVERITY =          STS$K_SEVERE,
                       CODE =              uf$fwf,
%(318)%                FACILITY =          $err,
                       MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%                optional_message =  (FAC=$mon),
                       optional_data =     .error,
                       ID=.msg[msg_unique_id]);

                kleanup(cleanup_vector);
                RETURN $reject;
                END;
            written = .written + .linlen;
            END;

        IF .linlen NEQ 0
        THEN
            BEGIN
%(318)%     $error(FACILITY =          $err,
%(318)%            SEVERITY =          STS$K_SEVERE,
                   CODE =              uf$frf,
                   MESSAGE_DATA =      CH$PTR(.msg[msg_fil_spec]),
%(318)%            OPTIONAL_MESSAGE =  (FAC=$mon),
                   OPTIONAL_DATA =     .error,
                   ID=.msg[msg_unique_id]);

            kleanup(cleanup_vector);
            RETURN $reject;
            END;

        IF NOT mx$file_close(.mailfil, file_keep, error)
        THEN
            BEGIN
%(318)%     $error(FACILITY =          $err,
%(318)%            SEVERITY =          STS$K_SEVERE,
                   CODE =              uf$fcf,
                   MESSAGE_DATA =      CH$PTR(dirbuf),
%(318)%            OPTIONAL_MESSAGE =  (FAC=$mon),
                   OPTIONAL_DATA =     .error,
                   ID=.msg[msg_unique_id]);

            mailfil = 0;
            kleanup(cleanup_vector);
            RETURN $reject;
            END;

        mx$file_close(.msgfil, file_abort, error);

        blast_tty(USER = CH$PTR(nambuf),
                  TEXT = ('You have mail from %A',
                          CH$PTR(.msg[msg_sender_string])) );
        END
    ELSE                                ! Over quota
        BEGIN
        kleanup(cleanup_vector);

            IF NOT .list[lst_quota]
            THEN
                BEGIN
                $TRACE_ALWAYS(.msg[msg_unique_id],
                     %STRING('%A is overquota (%D/%D) (p/w)%/',
                     '                          %D blocks in use.'),
                     CH$PTR(nambuf),.pqta,.wqta,.used);

                list[lst_quota] = 1;
                END;

        blast_tty(USER = CH$PTR(nambuf),
                  TEXT = (%STRING('You have undeliverable mail from %A.',
                                '%/  The mail is %D blocks long and it ',
                                'would put you over quota.'),
                          CH$PTR(.msg[msg_sender_string]),
                          .msg_size) );
        state = $defer;
        END;

    ufddel(.profile);
    %FI !END of TOPS10 conditional...

    RETURN .state
    END;

%global_routine('kleanup',vec: REF vector): NOVALUE =
    BEGIN
    LOCAL
        error;

    BIND
        msg_file = vec[0],
        mai_file = vec[1],
        profile  = vec[2];

    IF .msg_file GTR 0
    THEN
        mx$file_close(.msg_file, file_abort, error);

    IF .mai_file GTR 0
    THEN
        mx$file_close(.mai_file, file_abort, error);

    %IF %SWITCHES(TOPS10) %THEN
    IF .profile GTR 0
    THEN
        ufddel(.profile);
    %FI
    END;

%global_routine('GET_HEADER', ptr, msglen, sndptr) =
    BEGIN
    STACKLOCAL
        buffer: VECTOR[CH$ALLOCATION(30)] INITIAL(REP CH$ALLOCATION(30) OF
                                                    (0));

    msglen = .msglen + 10 + CH$LEN(.sndptr,max_string_length);

    %IF %SWITCHES(TOPS20) %THEN
        $$odtim(CH$PTR(buffer),-1,ot_tmz);
    %ELSE
	udtdat(-1,buffer);
    %FI

    $nmu$text(ptr,max_string_length,'%A,%(11)U;000000000000%/Sender: %A%/',
        CH$PTR(buffer),.msglen,.sndptr) - 1

    END;

%routine('GET_NAME', adr) =
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    BIND
        mptr = CH$PTR(UPLIT(%ASCIZ 'MAIL.TXT.1')),
        mlen = %CHARCOUNT(%ASCIZ 'MAIL.TXT.1');

    LOCAL
        len;

    len = CH$LEN(CH$PTR(.adr),max_string_length);
    CH$MOVE(mlen,mptr,CH$PLUS(CH$PTR(.adr),.len));
    RETURN .len

    %ELSE
    BIND
	buf = .adr;

    LOCAL
	ptr,
	ppn;

    ppn = .buf;
    ptr = CH$PTR(buf);
    RETURN $nmu$text(ptr, max_string_length, 'DSK:MAIL.TXT[%O,%O]',
		.ppn<18,18,0>, .ppn<0,18,0>);

    %FI
    END;

%routine('GET_QUOTA', ptr, perm_, working_, used_):NOVALUE =
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    BIND
        perm = .perm_,
        working = .working_,
        used = .used_;

    declare_jsys(rcdir,gtdal);
    LOCAL
        dirnum,
        rcbits;

    $$rcdir(rc_emo,CH$PTR(.ptr),0; rcbits,,dirnum);
    IF (.rcbits AND rc_nom) EQL 0
    THEN
        $$gtdal(.dirnum; working, used, perm);

    RETURN 0;
    %ELSE
    !NOTE ptr contains the ppn, and perm contains the SIXBIT structure name
    BIND
	perm = .perm_,
	working = .working_,
	used = .used_;

    quotas(.ptr,.perm; working, perm, used)

    %FI
    END;

%global_routine('NAG', too, msg): NOVALUE =
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    declare_jsys(ttmsg,odtim,getji,rcusr,mtopr,getab);

    LOCAL
        jobs,
        rcbits,
        userno,
        tim,
        sysmsg,
        ptr;

    LITERAL
        jibufsiz = 9;

    STACKLOCAL
        jibuf:  VECTOR[jibufsiz],
        buffer: VECTOR[CH$ALLOCATION(30)],
        msgbuf: VECTOR[CH$ALLOCATION(max_string_length)];

    tim = CH$PTR(buffer);
    CH$WCHAR_A(%O'7',tim);
    $$odtim(.tim,-1,ot_nda);

    ptr = CH$PTR(msgbuf);
    $nmu$text(ptr,max_string_length,'%/[%A %A - %A]%/',
            CH$PTR(nodnam), CH$PTR(buffer), .msg);

    IF .too EQL -1
    THEN
        $$ttmsg(-1, CH$PTR(msgbuf))
    ELSE
        BEGIN
        IF NOT $$getab(actjob;jobs)
        THEN
            BEGIN                           !Just in case we're NOT running 7.0
            jobs<left_half> = 1;            !Minimum jobnumber
            jobs<right_half> = 256;         !Worst case maximum
            END;

        $$rcusr(rc_emo,.too,0;rcbits,,userno);
        IF NOT (.rcbits AND (rc_dir OR rc_nom)) EQL 0
        THEN
            RETURN 0;

        INCR jobnum FROM .jobs<left_half> TO .jobs<right_half> DO
            BEGIN
            REGISTER
                t1 = 1;

            sysmsg = 0;
            IF $$getji(.jobnum,(-jibufsiz^18)+jibuf,$jitno)
            THEN
                BEGIN
                IF .jibuf[$jiuno-$jitno] EQL .userno            !Right user?
                THEN
                    IF .jibuf[$jitno-$jitno] GTR 0              !Yes, Detached?
                    THEN
!                        IF .jibuf[$jicpj-$jitno] LSS 0          !No, PTY?
!                        THEN
!                            IF .jibuf[$jibat-$jitno] GEQ 0      !No, Batch?
!                            THEN
                                BEGIN
                                $$mtopr(%O'400000' + .jibuf[$jitno-$jitno],
                                        $mornt;sysmsg);
                                IF .sysmsg EQL 0
                                THEN
                                    $$ttmsg(%O'400000' + .jibuf[$jitno-$jitno],
                                            CH$PTR(msgbuf));
                                jibuf[$jitno-$jitno] = 0;
                                END;
                END
            ELSE
                IF .t1 EQL gtjix3
                THEN
                    EXITLOOP;
            END;
        END;
    RETURN 0;
    %ELSE
    BIND
	ppn = too;

    STACKLOCAL
        tbuf:   VECTOR[CH$ALLOCATION(40)],
        msgbuf: VECTOR[CH$ALLOCATION(max_string_length)];

    LOCAL
	ptr,
	jobnum;


    udttim(-1,tbuf);

    ptr = CH$PTR(msgbuf);
    $nmu$text(ptr,max_string_length,'%C%/[%A %A - %A]%/',
	%O'7', CH$PTR(nodnam), CH$PTR(tbuf), .msg);

    IF .too EQL - 1
    THEN
        RETURN 0    !*** PUT "blast to everyone" code here
    ELSE
        BEGIN
        REGISTER
            jobnum;

        jobnum = 0;
        WHILE fndusr(.ppn,.jobnum; jobnum) DO
            spltty(msgbuf,.jobnum);
        END;
    %FI
    END;
%routine('LOGGED_IN', ptr, jobno_) =
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    BIND
        jobno = .jobno_;

    declare_jsys(rcusr,getji,getab);
    LITERAL
        jibufsiz = 9;

    STACKLOCAL
        jibuf:  VECTOR[jibufsiz];
    LOCAL
        jobs,
        rcbits,
        userno;

    IF NOT $$getab(actjob;jobs)
    THEN
        BEGIN                           !Just in case we're NOT running 7.0
        jobs<left_half> = 1;            !Minimum jobnumber
        jobs<right_half> = 256;         !Worst case maximum
        END;

    $$rcusr(rc_emo,.ptr,0;rcbits,,userno);
    IF (.rcbits AND (rc_dir OR rc_nom)) EQL 0
    THEN
        BEGIN
        INCR i FROM .jobs<left_half> TO .jobs<right_half> DO
            BEGIN
            REGISTER t1=1;
            IF $$getji(.i,(-jibufsiz^18)+jibuf,$jitno)
            THEN
                BEGIN
                IF .jibuf[$jiuno-$jitno] EQL .userno            !Right user?
                THEN
                    IF .jibuf[$jitno-$jitno] GTR 0              !Yes, Detached?
                    THEN
                        IF .jibuf[$jicpj-$jitno] LSS 0          !No, PTY?
                        THEN
                            IF .jibuf[$jibat-$jitno] GEQ 0      !No, Batch?
                            THEN
                                BEGIN                           !Found him!
                                jobno = .i;
                                RETURN 1;
                                END
                END
            ELSE
                IF .t1 EQL gtjix3
                THEN
                    EXITLOOP;

            END;
        END;
    jobno = -1;
    RETURN 0;

    %ELSE !Begin TOPS-10 Conditional

    BIND
	ppn = ptr,
	jobno = .jobno_;

    IF NOT fndusr(.ppn, 0; jobno)
    THEN
	BEGIN
	jobno = -1;
	RETURN 0
	END
    ELSE
	RETURN 1;
    %FI
    END;

%global_routine('MX$VALIDATE_LOCAL_USER', len, namptr, pobptr) =
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    declare_jsys(rcdir,lnmst);

    STACKLOCAL
        rcbits,
        nambuf: VECTOR[CH$ALLOCATION(max_local_user_length)];

    OWN
        logbuf: VECTOR[CH$ALLOCATION(256)];

    LOCAL
        sptr,
        eptr,
        ptr,
        done,
        err;

    BIND
        pobox = CH$PTR(UPLIT(%ASCIZ'POBOX')),
        poboxc = CH$PTR(UPLIT(%ASCIZ'POBOX:')),
        logptr = CH$PTR(logbuf);

    IF CH$RCHAR(.namptr) EQL %C'"'
    THEN
        BEGIN
        len = .len - 2;
        namptr = CH$PLUS(.namptr,1);
        END;

    IF NOT $$lnmst($lnssy,pobox,logptr;err)     
    THEN
        IF .err EQL lnstx1                  !Not a logical name
        THEN
            CH$MOVE(7,poboxc,logptr)        !POBOX: might be a real structure
        ELSE
%(318)%     $error( SEVERITY = STS$K_SEVERE,
%(318)%             FACILITY = $err,
                    CODE = mx$ctp,
%(318)%             OPTIONAL_MESSAGE = (FAC=$mon),
                    OPTIONAL_DATA = .err);

    CH$TRANSLATE(uc_tab,
                 .len, .namptr,
                 0,
                 .len+1, CH$PTR(nambuf));

    IF CH$EQL(7,CH$PTR(UPLIT(%ASCIZ'SYSTEM')),
              7, CH$PTR(nambuf),
              0)
    THEN
        BEGIN
        CH$MOVE(%CHARCOUNT('POBOX:<SYSTEM>') + 1,
                CH$PTR(UPLIT(%ASCIZ'POBOX:<SYSTEM>')),
                CH$PTR(.pobptr));
        RETURN -1;
        END;

    eptr = sptr = logptr;
    done = $false;
    WHILE NOT .done DO
        BEGIN
        IF CH$RCHAR_A(eptr) EQL %C':'
        THEN
            BEGIN
            IF CH$RCHAR(.eptr) EQL 0
            THEN
                done = $true
            ELSE
                CH$WCHAR(0,.eptr);

            ptr = CH$PTR(.pobptr);
            $nmu$text(ptr,max_string_length,'%A<%A>',.sptr,CH$PTR(nambuf));

            IF NOT $$rcdir(rc_emo,CH$PTR(.pobptr),0;
                           rcbits)
            THEN
                EXITLOOP;

            IF ((.rcbits AND (rc_dir OR rc_nom OR rc_amb)) EQL 0)
            THEN
                RETURN $true;

            sptr = (eptr = CH$PLUS(.eptr,1));
            END
        END;

    mx$update_pobox_status();
    IF .pobsts EQL 1
    THEN
        RETURN 0
    ELSE
        RETURN 3

    %ELSE !  Tops 10
    STACKLOCAL
        nambuf: VECTOR[CH$ALLOCATION(max_local_user_length)];

    BIND
	pobadr = .pobptr;

![303] ADD CHECK FOR QUOTED STRING
    IF CH$RCHAR(.namptr) EQL %C'"'
    THEN
        BEGIN
        len = .len - 2;
        namptr = CH$PLUS(.namptr,1);
        END;

    CH$WCHAR(0, CH$MOVE(.len, .namptr, CH$PTR(nambuf,0,8)));
    RETURN (namppn(nambuf; pobadr));
    %FI
    END;
![307] ADD PARAMETER INVFLG AND ROUTINE COMMENT
%global_routine('MX$GET_USERNAME', uid, vflg) =   ![307]
    BEGIN
!++
! FUNCTIONAL DESCRIPTION:
!
!       This routine returns an asciz string representing the userid.  On
!   TOPS-20 this is the output of a DIRST. On TOPS-10, this is either the
!   username from the accounting system, or his PPN converted to a string if no
!   accounting entry exists for the PPN.  If the resulting string contains
!   special characters, quotes are put around the offending string. An @
!   followed by the local node name is appended to the string.
!       In addition, MX's internal servers generate UID's that look like
!   <-1,,ADDR> where ADDR is the address of an ASCIZ string. In this case, a
!   copy of the asciz string beginning at ADDR will be returned.  This is used
!   by MX's internal servers (such as MAIL-11 and SMTP) to set up appropriate
!   log file entries.
!
! FORMAL PARAMETERS:
!
!	UID:
!           TOPS-20: a user number/directory number suitable for DIRST or
!                    <-1,,ADDR>
!           TOPS-10: a PPN or <-1,,ADDR>
!
!       VFLG:   An address in which to return 1 if this is a valid user, and 
!               0 otherwise.
!
! IMPLICIT INPUTS:
!
!    	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!--

![303] ADD SPECIAL-CHARACTER TRANSLATION TABLE
![306] ADD "." TO SPECIAL-CHARACTER TABLE
    BIND
        trntbl = CH$TRANSTABLE(special_characters('	 "(),.;<>@[\]'));

![314] All the possible USERNAME strings:
![314]  - "User Name"@NODE
![314]  - USERNAME@NODE
![314]  - "User Name"
![314]  - USERNAME

    BIND                                                    ![314]
        net_quotes =     UPLIT(%ASCIZ %STRING('"%A"@%A')),  ![314]
        net_noquotes =   UPLIT(%ASCIZ %STRING('%A@%A')),    ![314]
        nonet_quotes =   UPLIT(%ASCIZ %STRING('"%A"')),     ![314]
        nonet_noquotes = UPLIT(%ASCIZ %STRING('%A'));       ![314]

    STACKLOCAL                                              ![314]
        user_string;                                        ![314]

    %IF %SWITCHES(TOPS20) %THEN
    declare_jsys(dirst);

    LOCAL
        ptr,
        nambuf: VECTOR[CH$ALLOCATION(max_string_length)],
        tmpbuf: VECTOR[CH$ALLOCATION(max_string_length)],
        quote,
        len;            ![306]

    .vflg = $true;      ![307] INITIALIZE VFLG TO TRUE

    IF .uid LSS 0
    THEN
        RETURN copy_asciz(CH$PTR(.uid<0,18,0>));

![306] CALCULATE LENGTH OF USERNAME AND USE IT
    uid = .uid OR %O'500000000000';
    $$dirst(CH$PTR(nambuf), .uid; ptr);     ![306] Save updated byte pointer

    len = CH$DIFF(.ptr,CH$PTR(nambuf));     ![306]

![307] SET VFLG TRUE IF VALID, FALSE OTHERWISE
    .vflg = mx$validate_local_user(.len, CH$PTR(nambuf), tmpbuf);
    
![303] QUOTE THE USERNAME IF IT CONTAINS SPECIAL CHARACTERS
    ptr = CH$PTR(tmpbuf);

    CH$TRANSLATE(trntbl,
                 .len, CH$PTR(nambuf), 0,   ![306]
                 .len, .ptr);               ![306]

    quote = CH$FIND_CH(.len,.ptr,1);

![314] Pick the correct format username string
    user_string = (IF CH$FAIL(.quote)
                   THEN
                   IF .nodnam NEQ 0 THEN net_noquotes
                                    ELSE nonet_noquotes
                   ELSE
                       IF .nodnam NEQ 0 THEN net_quotes
                                        ELSE nonet_quotes);

    len = $nmu$text(ptr,max_string_length,'%I', ![314]Format the username
                CH$PTR(.user_string),           ![314]The control string
                CH$PTR(nambuf),                 ![314]The user name
                CH$PTR(nodnam));                ![314]The node name

    RETURN
        copy_asciz(CH$PTR(tmpbuf));

    %ELSE
    STACKLOCAL
        tmpbuf: VECTOR[CH$ALLOCATION(max_string_length)],
        len,
        ptr,
	adr,
        quote;      ![303]

    .vflg = $true;  ![307] SET VFLG TRUE

    IF .uid LSS 0
    THEN
        RETURN copy_asciz(CH$PTR(.uid<0,18,0>));

    IF ppnnam(uid; adr)         !Adr contains the address of the user's profile
    THEN
        BEGIN
        ptr = CH$PTR(tmpbuf);

![303] QUOTE THE USERNAME IF IT CONTAINS SPECIAL CHARACTERS
        CH$TRANSLATE(trntbl,
                     39, CH$PTR(.adr,0,8), 0,
                     39, .ptr);

        quote = CH$FIND_CH(39,.ptr,1);
 
![314] Pick the right username format...
        user_string = (IF CH$FAIL(.quote)
                       THEN
                       IF .nodnam NEQ 0 THEN net_noquotes
                                        ELSE nonet_noquotes
                       ELSE
                           IF .nodnam NEQ 0 THEN net_quotes
                                            ELSE nonet_quotes);

        len = $nmu$text(ptr,max_string_length,'%I', ![314]Format the username
                    CH$PTR(.user_string),           ![314]The control string
                    CH$PTR(.adr,0,8),               ![314]The user name
                    CH$PTR(nodnam));                ![314]The node name

	RETURN copy_string(CH$PTR(tmpbuf),
       		           CH$LEN(CH$PTR(tmpbuf), max_string_length) + 1);
        END

![307] ADD "ELSE" CLAUSE SO THAT ALL USERS GET A STRING...
    ELSE
        BEGIN
        .vflg = $false;
        ptr = CH$PTR(tmpbuf);
        $nmu$text(ptr,max_string_length,'"[%O,%O]"',
            .uid<18,18,0>,
            .uid<0,18,0>);
        RETURN copy_asciz(CH$PTR(tmpbuf));
        END;
    %FI
    END;

%global_routine('MX$BUILD_REPAIR_SPEC', msg: REF message_table_entry)=
    BEGIN
    %IF %SWITCHES(TOPS20) %THEN
    STACKLOCAL
        user_buffer: VECTOR[CH$ALLOCATION(50)];

    LOCAL
        len,
        ptr;

    BIND
        p1 = CH$PTR(UPLIT('POBOX:<')),
        p2 = CH$PTR(UPLIT('>')),
        ps = CH$PTR(.msg[msg_sender_string]),
        pe = CH$PTR(.msg[msg_env_spec],3),
        pbuf = CH$PTR(user_buffer);

    ptr = CH$FIND_CH(31, ps, %C'@');
    IF CH$FAIL(.ptr)
    THEN
        len = CH$LEN( ps )
    ELSE
        len = CH$DIFF( .ptr, ps);

    CH$COPY(%CHARCOUNT('POBOX:<'),  p1,
            .len,                   ps,
            1,                      p2,
            CH$LEN(pe),             pe,
            0,
            50,                     pbuf);
    
    copy_string(pbuf, CH$LEN(pbuf) + 1)
    %ELSE
    0
    %FI
    END;

GLOBAL ROUTINE getdat(ptr) =
    BEGIN
    %IF %SWITCHES(TOPS10) %THEN
    STACKLOCAL
        datbuf: VECTOR[CH$ALLOCATION(30)];

    LOCAL
	ch,
	p;

    udtdat(-1,datbuf);
    p = CH$PTR(datbuf);
    WHILE (ch = CH$RCHAR_A(p)) NEQ 0 DO CH$WCHAR_A(.ch, ptr);
    RETURN .ptr;
    %ELSE 0
    %FI
    END;

%IF %SWITCHES(TOPS10) %THEN
%global_routine('MXUFDE', prefix, text): UFD1 NOVALUE =
    BEGIN
    log(.text,0)    %(318)%
    END;
%FI

%global_routine('MX$TIME_TIL_SHUTDOWN') =
!This routine is not yet implemented...
    BEGIN
    RETURN 1234567;
    END;
END
ELUDOM