?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network_access: Application Event Processor' ??
MODULE nam$application_event_processor;
?? RIGHT := 110 ??

?? NEWTITLE := '  Global Declarations Referenced by this module' ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$local_file_name
*copyc avt$validation_items
*copyc clc$standard_file_names
*copyc clt$parameter_list
*copyc clt$value
*copyc cle$ecc_command_processing
*copyc clt$command_line_size
*copyc clt$line_layout
*copyc ife$error_codes
*copyc ift$format_effectors
*copyc ift$connection_attributes
*copyc jmt$job_class_name
*copyc jmt$job_submission_options
*copyc jmt$system_supplied_name
*copyc jmt$user_supplied_name
*copyc nac$application_catalog_layout
*copyc nac$statistics_codes
*copyc nae$application_interfaces
*copyc nae$application_management
*copyc nae$client_validation_dialog
*copyc nat$am_login_prompt
*copyc nat$create_attributes
*copyc nat$network_address
*copyc nat$number_of_connections
*copyc nat$protocol
*copyc nat$sap_identifier
*copyc nat$server_attributes
*copyc nlt$cl_connection
*copyc nlt$ta_sap_selector
*copyc osc$timesharing
*copyc osd$unique_name
*copyc ost$max_status_message_line
*copyc ost$status_message
*copyc ost$status_message_header_kind
*copyc ost$status_message_level
*copyc ost$system_flag
*copyc ost$user_identification
*copyc pmt$program_parameters
?? POP ??
*copyc amp$flush
*copyc amp$get_next
*copyc amp$put_next
*copyc amp$put_partial
*copyc amp$return
*copyc avp$prevalidate_job
*copyc clp$get_login_data_for_nam
*copyc clp$pop_parameters
*copyc clp$push_parameters
*copyc clp$trimmed_string_size
*copyc clp$validate_name
*copyc fsp$close_file
*copyc fsp$open_file
*copyc i#move
*copyc iip$direct_store_trm_conn_atts
*copyc ifp$suppress_cursor_pos_echoplx
*copyc jmp$get_attribute_defaults
*copyc jmp$signal_pair_connect_target
*copyc jmp$submit_job
*copyc jmp$validate_paired_connection
*copyc nap$cancel_switch_offer
*copyc nap$create_network_file
*copyc nap$display_message
*copyc nap$find_server_attributes
*copyc nap$get_attributes
*copyc nap$parse_accounting_data
*copyc nap$offer_connection_switch
*copyc nap$remove_connection_id
*copyc nap$store_client_identity
*copyc nlp$get_exclusive_access
*copyc nlp$get_nonexclusive_access
*copyc nlp$release_exclusive_access
*copyc nlp$release_nonexclusive_access
*copyc nlp$se_accept_connection
*copyc osp$format_multi_part_message
*copyc osp$i_await_activity_completion
*copyc osp$output_status_message
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc pfp$attach
*copyc pmp$generate_unique_name
*copyc pmp$get_executing_task_gtid
*copyc pmp$log_ascii
*copyc pmp$wait
*copyc pmp$ready_task
*copyc rmp$request_terminal
*copyc sfp$emit_statistic
*copyc oss$job_paged_literal
*copyc oss$task_shared
*copyc osv$task_private_heap
*copyc osv$task_shared_heap
*copyc jmv$executing_within_system_job
*copyc nav$application_mgmt_taskid
*copyc nav$final_login_prompt
*copyc nav$force_password_prompt
*copyc nav$maximum_login_attempts
*copyc nav$network_paged_heap
*copyc nav$prompt_for_account
*copyc nav$prompt_for_family_name
*copyc nav$prompt_for_project
*copyc nav$server_attributes_list
?? OLDTITLE ??
?? NEWTITLE := '  Global Declarations Declared by this module', EJECT ??

  TYPE
    am_login_specification = (am_incomplete, am_via_string, am_via_parameters),
    am_client_validation_kind = (am_client_dialog, am_remote_attach_job),
    am_remote_attach_job_state = (am_validate_job, am_connection_switch_offered, am_server_inactive,
          am_validation_failed, am_switch_failed, am_signal_failed),

    am_received_connections_list = record
      fill: 0 .. 0ffff(16),
      received_connection: ^am_received_connection_attr,
    recend,

    am_received_connection_attr = record
      next_entry: ^am_received_connection_attr,
      server: nat$application_name,
      server_job_validation_source: nat$server_validation_source,
      activity_stamp: integer,
      connection_id: nat$connection_id,
      network_file_name: amt$local_file_name,
      peer_accounting_info_length: nat$data_length,
      peer_accounting_info: SEQ (REP jmc$job_input_device_size of cell),
      case client_validation_kind: am_client_validation_kind of
      = am_client_dialog =
        terminal_file_name: amt$local_file_name,
        file_id: amt$file_identifier,
        retry_count: 0 .. 0ff(16),
        last_prompt: nat$am_login_prompt,
        login_parameters: am_login_parameters,
        login_string: ^SEQ ( * ),
        prompt_user: boolean,
        send_previous_prompt: boolean,
      = am_remote_attach_job =
        state: am_remote_attach_job_state,
        service_data_ii_length: nat$data_length,
        service_data_ii: string (128),
      casend,
    recend,

    am_login_parameters = record
      retry_count_for_login_prompt: 0 .. 0ff(16),
      user_name: ost$user_name,
      password: ost$name,
      family_name: ost$family_name,
      account_name: avt$account_name,
      project_name: avt$project_name,
    recend,

    osi_nsap_address = record
      subnetwork: 0 .. 0ffff(16),
      system: 0 .. 0ffffffffffff(16),
      nsap: 0 .. 0ff(16),
    recend,

    cdna_system_id = packed record
      prefix: 0 .. 0ffffff(16),
      system_type: 0 .. 3,
      cpu_0_model_number: 0 .. 0ff(16),
      cpu_0_serial_number: 0 .. 3fff(16),
    recend;

  VAR
    nav$received_connections_list: [XDCL, oss$task_shared] am_received_connections_list := [0, NIL];

  VAR
    connections_in_dialog: ^am_received_connection_attr := NIL,
    am_login_template: [READ, STATIC, oss$job_paged_literal] am_login_parameters :=
          [0, osc$null_name, osc$null_name, osc$null_name, osc$null_name, osc$null_name],
    final_login_prompt: nat$am_login_prompt,
    formatted_prompt: array [nat$am_login_prompt] of ^string ( * ),
    include_prompt: array [nat$am_login_prompt] of boolean := [ * , TRUE, TRUE, * , * , * ],
    raw_prompt: [READ, STATIC, oss$job_paged_literal] array [nat$am_login_prompt] of
          ost$status_condition := [nae$login_banner, nae$user_prompt, nae$password_prompt, nae$family_prompt,
          nae$account_prompt, nae$project_prompt];

  VAR
    login_blank_overwrite: [READ, oss$job_paged_literal] string (overwrite_pattern_size) := ' ',
    login_overwrite_pattern: [READ, oss$job_paged_literal] string (overwrite_pattern_size + 2) :=
          'HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI#HI';

  CONST
    suppress_cursor_positioning = TRUE,
    suppress_echoplex = TRUE,
    overwrite_pattern_size = 3 * 30, {Must be a multiple of 3.}
    max_job_submission_options = 11;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$find_sap_priority', EJECT ??

  PROCEDURE [XDCL] nap$find_sap_priority
    (    sap_id: nat$sap_identifier;
     VAR priority: nat$network_message_priority);


{ This procedure executes in the system task.
{ This procedure will be called by the session internal
{ interface when an osi transport connect indication
{ occurs.

    VAR
      server_attributes: ^nat$server_attributes;

    nlp$get_nonexclusive_access (nav$server_attributes_list.access_control);
    get_server_attributes (sap_id, server_attributes);
    IF server_attributes <> NIL THEN
      priority := server_attributes^.message_priority;
    ELSE
      priority := nac$default_message_priority;
    IFEND;
    nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);

  PROCEND nap$find_sap_priority;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$process_connect_indication', EJECT ??

  PROCEDURE [XDCL] nap$process_connect_indication
    (    sap_id: nat$sap_identifier;
         connection_id: nat$connection_id;
         cl_connection: ^nlt$cl_connection;
         source_address: nat$network_address;
     VAR server: nat$application_name;
     VAR protocol: nat$protocol;
     VAR status: ost$status);


{ This procedure executes in the system task. This is the event handler that
{ is invoked whenever a sap event is received by the underlying layer.
{ If the received connection is to be validated on behalf of the application,
{ the connection is assigned to the application mgmt task. Otherwise the
{ connection is assigned to the server job. A new job is  initiated if the
{ existing server jobs are unable to acquire more connections.
{ NOTE the parameter SERVER is set even if the status returned is abnormal.
{ The exception is if the server_attributes is NIL i.e. the connect event
{ is for an unknown server.

    VAR
      actual_access_control: am_received_connections_list,
      client_addresses: ^array [1 .. * ] of nat$client_address,
      client_address: ^nat$client_address,
      client_system: cdna_system_id,
      cs_status: osc$cs_successful .. osc$cs_variable_locked,
      i: integer,
      new_access_control: am_received_connections_list,
      nsap_address: ^osi_nsap_address,
      nsap_address_prefix: ^SEQ ( * ),
      original_access_control: am_received_connections_list,
      osi_network_address: ^SEQ ( * ),
      received_connection: ^am_received_connection_attr,
      server_attributes: ^nat$server_attributes,
      server_connection: ^nat$server_connection_attribute,
      tsap_selector: nlt$ta_sap_selector,
      valid_client_address: boolean;


    status.normal := TRUE;
    nlp$get_nonexclusive_access (nav$server_attributes_list.access_control);
    get_server_attributes (sap_id, server_attributes);
    IF (server_attributes = NIL) THEN { SAP may have been deactivated.
      osp$set_status_condition (nae$application_inactive, status);
      protocol := nac$cdna_virtual_terminal;
    ELSE
      nlp$get_exclusive_access (server_attributes^.access_control);
      server := server_attributes^.server;
      protocol := server_attributes^.protocol;
      IF server_attributes^.server_status = nac$application_inactive THEN
        osp$set_status_abnormal (nac$status_id, nae$application_inactive, server_attributes^.server, status);
      ELSE
        server_attributes^.attempted_connection_count := server_attributes^.attempted_connection_count + 1;
        IF server_attributes^.connection_count >= server_attributes^.max_connections THEN
          server_attributes^.rejected_connection_attempts :=
                server_attributes^.rejected_connection_attempts + 1;
          osp$set_status_abnormal (nac$status_id, nae$application_max_conn_limit, server_attributes^.server,
                status);
        ELSE
          valid_client_address := TRUE;

          IF (source_address.kind = nac$osi_transport_address)
{       } AND (server_attributes^.client_addresses <> NIL) THEN
            valid_client_address := FALSE;
            client_addresses := server_attributes^.client_addresses;
            osi_network_address := ^source_address.osi_transport_address.network_address;
            RESET osi_network_address;
            IF source_address.osi_transport_address.network_address_length > #SIZE (osi_nsap_address) THEN
              NEXT nsap_address_prefix: [[REP (source_address.osi_transport_address.
                    network_address_length - #SIZE (osi_nsap_address)) OF cell]] IN osi_network_address;
            IFEND;
            NEXT nsap_address IN osi_network_address;
            IF nsap_address <> NIL THEN
              #UNCHECKED_CONVERSION (nsap_address^.system, client_system);
              IF source_address.osi_transport_address.transport_sap_selector_length =
                    #SIZE (tsap_selector) THEN
                #UNCHECKED_CONVERSION (source_address.osi_transport_address.
                      transport_sap_selector (1, #SIZE (tsap_selector)), tsap_selector);
              ELSE
                tsap_selector := 0;
              IFEND;

            /match_client_address/
              FOR i := 1 TO UPPERBOUND (client_addresses^) DO
                client_address := ^client_addresses^ [i];
                IF (((client_address^.network_id = 0) OR (client_address^.network_id =
                      nsap_address^.subnetwork)) AND ((NOT client_address^.reserved_application_id) OR
                      (client_address^.application_id = tsap_selector))) THEN
                  CASE client_address^.system_kind OF
                  = nac$nosve_system_kind =
                    valid_client_address := (client_system.prefix = 080025(16) {CDC} ) AND
                          (client_system.system_type = 3 {NOS/VE system type} );
                  = nac$cdcnet_system_kind =
                    valid_client_address := (client_system.prefix = 080025(16) {CDC} ) AND
                          (client_system.system_type < 3 {CDCNET system type} );
                  = nac$any_system_kind =
                    valid_client_address := (client_address^.system_id = 0) OR
                          (client_address^.system_id = nsap_address^.system);
                  ELSE
                  CASEND;
                  IF valid_client_address THEN
                    EXIT /match_client_address/; {----->
                  IFEND;
                IFEND;
              FOREND /match_client_address/;
            IFEND;
          IFEND;

          IF NOT valid_client_address THEN

{ *** DEBUG pmp$log ('AM - Connection from an invalid client address.', ignore_status);

            osp$set_status_abnormal (nac$status_id, nae$unknown_application, server, status);
          ELSE
            IF server_attributes^.accept_connection THEN
              nlp$se_accept_connection (cl_connection, {ignore} status);
              status.normal := TRUE;
            IFEND;

            IF ((server_attributes^.nam_initiated_server) AND
                  (server_attributes^.server_job_validation_source = nac$client)) OR
                  (server_attributes^.client_validation_capability <> osc$null_name) THEN
              ALLOCATE received_connection IN osv$task_shared_heap^;
              received_connection^.server := server_attributes^.server;
              received_connection^.server_job_validation_source :=
                    server_attributes^.server_job_validation_source;
              received_connection^.connection_id := connection_id;
              received_connection^.network_file_name := osc$null_name;
              received_connection^.client_validation_kind := am_client_dialog;
              received_connection^.terminal_file_name := osc$null_name;
              received_connection^.retry_count := 0;
              received_connection^.login_parameters := am_login_template;
              received_connection^.peer_accounting_info_length := 0;
              received_connection^.login_string := NIL;

              REPEAT
                ALLOCATE server_connection IN nav$network_paged_heap^;
                IF server_connection = NIL THEN
                  syp$cycle;
                IFEND;
              UNTIL server_connection <> NIL;
              server_connection^.connection_id := connection_id;
              server_connection^.connection_kind := nac$in_dialog;
              server_connection^.next_entry := server_attributes^.server_connections_list;
              server_attributes^.server_connections_list := server_connection;
              server_attributes^.connection_count := server_attributes^.connection_count + 1;

              received_connection^.activity_stamp := #FREE_RUNNING_CLOCK (0);
              received_connection^.next_entry := NIL;
              new_access_control.fill := 0;
              new_access_control.received_connection := received_connection;
              original_access_control.fill := 0;
              original_access_control.received_connection := NIL;
              REPEAT
                #COMPARE_SWAP (nav$received_connections_list, original_access_control, new_access_control,
                      actual_access_control, cs_status);
                IF cs_status = osc$cs_failed THEN
                  original_access_control := actual_access_control;
                  received_connection^.next_entry := actual_access_control.received_connection;
                IFEND;
              UNTIL (cs_status = osc$cs_successful);
              pmp$ready_task (nav$application_mgmt_taskid, {ignore} status);
              status.normal := TRUE;

{ *** DEBUG   pmp$log ('AM CONNECTION ASSIGNED TO AM TASK', ignore_status);

            ELSE
              assign_received_connection (server_attributes, connection_id, status);
              IF status.normal THEN
                server_attributes^.connection_count := server_attributes^.connection_count + 1;
              IFEND;

              IF (NOT status.normal) AND (server_attributes^.nam_initiated_server) THEN

{ *** DEBUG     pmp$log ('AM - Initiation of nam init server job failed.', ignore_status);

                osp$set_status_abnormal (nac$status_id, nae$unknown_application, server, status);
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
      nlp$release_exclusive_access (server_attributes^.access_control);
    IFEND;

    nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);

  PROCEND nap$process_connect_indication;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$am_poll_connections' ??
?? NEWTITLE := '  output_status_message', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$am_poll_connections
    (    task_parameter_list: pmt$program_parameters;
     VAR status: ost$status);

{ This procedure is the main loop in the application mgmt task.
{ All the received connections are polled for input and the received
{ login parameters are analyzed. If all the login parameters have been
{ received, the connection is assigned to a saj and is removed from
{ the received connections list. It is assumed that the client dialog
{ is done only with a terminal user. On a define server request, it needs
{ to be verified that the connection request validation or client supplied
{ validation options are selected only with cdna virtual terminal protocol.


    PROCEDURE output_message
      (    file_id: amt$file_identifier;
           message: ost$status);

{ NOTE: This procedure exists solely to conserve stack sapce.

      VAR
        ignore_status: ost$status;

      osp$output_status_message (file_id, osc$brief_message_level, osc$subdued_status_message_hdr, message,
            ignore_status);
    PROCEND output_message;
?? OLDTITLE, EJECT ??

    CONST
      long_wait = 2000000, {2000 sec}
      short_wait = 500, {1/2 sec}
      login_timeout_interval = 120000000, {2 min in usec}
      ten_minutes = 600000000; {10 min in usec}

    VAR
      byte_address: amt$file_byte_address,
      client_identity: nat$client_identity,
      connection_attribute: array [1 .. 1] of nat$get_attribute,
      connection_switch_kind: jmt$paired_connection_request,
      decrement_rec_conn_count: boolean,
      file_position: amt$file_position,
      job: jmt$system_supplied_name,
      last_login_parameters: ^SEQ ( * ),
      login_parameters: ^SEQ ( * ),
      login_specification: am_login_specification,
      new_connections_list: ^am_received_connection_attr,
      next_received_connection: ^am_received_connection_attr,
      wait_list: array [1 .. 2] of ost$i_activity, { nac$i_await_switch_accept
      parameter_data: SEQ (REP clc$nominal_command_line_size of cell),
      parameter_list: ^clt$parameter_list,
      parameter_list_contents: ^string ( * ),
      parameter_list_size: ^clt$command_line_size,
      previous_received_connection: ^^am_received_connection_attr,
      prompt_name: string (9),
      ready_index: integer,
      received_connection: ^am_received_connection_attr,
      re_prompt: boolean,
      retry_limit_reached: boolean,
      return_files: boolean,
      server_attributes: ^nat$server_attributes,
      server_connection: ^nat$server_connection_attribute,
      remote_attach_job: ^SEQ ( * ),
      transfer_count: amt$transfer_count,
      wait_time: integer;

    IF NOT jmv$executing_within_system_job THEN
      RETURN; {----->
    IFEND;

    connection_attribute [1].kind := nac$connection_state;
    pmp$get_executing_task_gtid (nav$application_mgmt_taskid);

    format_login_prompts;

    login_parameters := ^parameter_data;
    RESET login_parameters;
    NEXT parameter_list_size IN login_parameters;
    NEXT parameter_list_contents: [clc$nominal_command_line_size - #SIZE (parameter_list_size^)] IN
          login_parameters;

    WHILE TRUE DO

{ Initiate dialogs on new connections. If no connections are currently being processed, process the new
{ connections immediately.

      initiate_new_dialogs (new_connections_list);
      IF connections_in_dialog = NIL THEN
        connections_in_dialog := new_connections_list;
        new_connections_list := NIL;
      IFEND;

      wait_time := long_wait;
      received_connection := connections_in_dialog;
      previous_received_connection := ^connections_in_dialog;

    /process_connection/
      WHILE (received_connection <> NIL) DO
        wait_time := short_wait;
        CASE received_connection^.client_validation_kind OF
        = am_client_dialog =
          re_prompt := FALSE;
          return_files := FALSE;
          decrement_rec_conn_count := TRUE;
          retry_limit_reached := FALSE;

          IF received_connection^.prompt_user THEN
            prompt_user (received_connection, retry_limit_reached, {ignore} status);
            received_connection^.prompt_user := FALSE;
            login_specification := am_incomplete;
          IFEND;
          IF NOT retry_limit_reached THEN
            amp$get_next (received_connection^.file_id, parameter_list_contents,
                  #SIZE (parameter_list_contents^), transfer_count, byte_address, file_position, status);

          /process_login_parameters/
            BEGIN

              IF NOT status.normal THEN
                IF (status.condition = ife$input_timeout_exceeded) OR
                      (status.condition = ife$no_data_available) THEN

{ Simulate ok status for input timeout.

                  IF (received_connection^.activity_stamp + login_timeout_interval) <=
                        #FREE_RUNNING_CLOCK (0) THEN
                    osp$set_status_condition (nae$login_timeout, status);
                    output_message (received_connection^.file_id, status);
                    return_files := TRUE;

{ *** DEBUG       pmp$log ('User Login Timeout.', ignore_status);

                  ELSE
                    previous_received_connection := ^received_connection^.next_entry;
                    received_connection := received_connection^.next_entry;
                  IFEND;
                ELSE
                  nap$display_message (status);
                  return_files := TRUE;
                IFEND;
                EXIT /process_login_parameters/; {----->
              IFEND;

              received_connection^.activity_stamp := #FREE_RUNNING_CLOCK (0);
              IF (file_position <> amc$eor) OR ((transfer_count <= 0) AND
                    ((received_connection^.last_prompt = nac$am_user_name) OR
                    (received_connection^.last_prompt = nac$am_password))) THEN

{ Re-request the input.

                amp$put_next (received_connection^.file_id, NIL, 0, byte_address, {ignore} status);
                IF received_connection^.last_prompt = nac$am_user_name THEN
                  prompt_name := 'user name';
                ELSE
                  prompt_name := 'password';
                IFEND;
                osp$set_status_abnormal (nac$status_id, nae$login_validation_required, prompt_name, status);
                output_message (received_connection^.file_id, status);
                IF received_connection^.login_parameters.retry_count_for_login_prompt <
                      (nav$maximum_login_attempts - 1) THEN
                  osp$set_status_condition (nae$retry_login, status);
                  output_message (received_connection^.file_id, status);
                IFEND;
                received_connection^.prompt_user := TRUE;
                received_connection^.send_previous_prompt := TRUE;
                CYCLE /process_connection/; {----->
              ELSE
                IF transfer_count > 0 THEN
                  extract_login_parameters (received_connection, transfer_count, login_parameters,
                        login_specification, re_prompt);
                ELSE
                  IF received_connection^.last_prompt = final_login_prompt THEN
                    login_specification := am_via_parameters;
                  IFEND;
                IFEND;

                IF login_specification <> am_incomplete THEN
                  amp$flush (received_connection^.file_id, osc$wait, {ignore} status);
                  assign_validated_connection (received_connection, transfer_count, login_parameters,
                        login_specification, status);
                  IF status.normal THEN

{ *** DEBUG       pmp$log ('AM LOGIN DIALOG COMPLETE', ignore_status);

                    decrement_rec_conn_count := FALSE;
                    return_files := TRUE;
                    EXIT /process_login_parameters/; {----->
                  IFEND;

                  output_message (received_connection^.file_id, status);

                  IF (status.condition = nae$application_inactive) THEN
                    decrement_rec_conn_count := TRUE;
                    return_files := TRUE;
                    EXIT /process_login_parameters/; {----->
                  IFEND;

{ Terminate the connection if job class limit is reached or if server is deleted.

                  IF (status.condition = jme$maximum_jobs) OR (status.condition = nae$unknown_application)
                        THEN
                    osp$set_status_condition (nae$null_message, status);
                    output_message (received_connection^.file_id, status);
                    return_files := TRUE;
                    EXIT /process_login_parameters/; {----->
                  IFEND;

                  IF received_connection^.retry_count >= (nav$maximum_login_attempts - 1) THEN
                    retry_limit_reached := TRUE;
                    EXIT /process_login_parameters/; {----->
                  IFEND;

                  osp$set_status_condition (nae$retry_login, status);
                  output_message (received_connection^.file_id, status);
                  received_connection^.login_parameters := am_login_template;
                  received_connection^.last_prompt := nac$am_login_banner;
                  received_connection^.retry_count := received_connection^.retry_count + 1;
                  re_prompt := FALSE;
                IFEND;

{   Depending on the information received send the prompt for the next required
{   validation information;

                received_connection^.prompt_user := TRUE;
                received_connection^.send_previous_prompt := re_prompt;
                CYCLE /process_connection/; {----->
              IFEND;

              IF NOT retry_limit_reached THEN
                previous_received_connection := ^received_connection^.next_entry;
                received_connection := received_connection^.next_entry;
              IFEND;

            END /process_login_parameters/;
          IFEND;

          IF retry_limit_reached THEN
            osp$set_status_condition (nae$retry_limit_at_login, status);
            output_message (received_connection^.file_id, status);
            IF received_connection^.login_string <> NIL THEN
              last_login_parameters := received_connection^.login_string;
            ELSE
              parameter_list_size^ := transfer_count;
              last_login_parameters := login_parameters;
            IFEND;
            report_unsuccessful_login (received_connection, last_login_parameters, login_specification);
            return_files := TRUE;
          IFEND;

          IF return_files THEN
            IF decrement_rec_conn_count THEN
              decrement_received_conn_count (received_connection^.server, received_connection^.connection_id);
            IFEND;

            fsp$close_file (received_connection^.file_id, {ignore} status);
            amp$return (received_connection^.terminal_file_name, {ignore} status);
            amp$return (received_connection^.network_file_name, {ignore} status);
            previous_received_connection^ := received_connection^.next_entry;
            next_received_connection := received_connection^.next_entry;
            IF received_connection^.login_string <> NIL THEN
              FREE received_connection^.login_string IN osv$task_shared_heap^;
            IFEND;
            FREE received_connection IN osv$task_shared_heap^;
            received_connection := next_received_connection;
          IFEND;
          status.normal := TRUE;

        = am_remote_attach_job =
          CASE received_connection^.state OF
          = am_validate_job =
            nlp$get_nonexclusive_access (nav$server_attributes_list.access_control);
            nap$find_server_attributes (received_connection^.server, server_attributes);
            IF server_attributes <> NIL THEN
              nlp$get_exclusive_access (server_attributes^.access_control);
              IF server_attributes^.server_status = nac$application_active THEN
                server_connection := server_attributes^.server_connections_list;
                WHILE (server_connection <> NIL) AND (server_connection^.connection_id <>
                      received_connection^.connection_id) DO
                  server_connection := server_connection^.next_entry;
                WHILEND;
                IF server_connection <> NIL THEN
                  server_connection^.connection_kind := nac$owned_by_server;
                IFEND;
                remote_attach_job := #SEQ (received_connection^.
                      service_data_ii (1, received_connection^.service_data_ii_length));
                RESET remote_attach_job;
                jmp$validate_paired_connection (remote_attach_job, client_identity.family,
                      client_identity.user, job, connection_switch_kind, status);
                IF status.normal THEN
                  IF connection_switch_kind = jmc$pcr_leveled_job_request THEN
                    assign_connect_for_leveled_job (received_connection, server_attributes, job);
                    nap$store_client_identity (received_connection^.connection_id, client_identity,
                          {ignore} status);
                    status.normal := TRUE;

                    amp$return (received_connection^.network_file_name, {ignore} status);
                    status.normal := TRUE;
                    previous_received_connection^ := received_connection^.next_entry;
                    next_received_connection := received_connection^.next_entry;
                    FREE received_connection IN osv$task_shared_heap^;
                    received_connection := next_received_connection;

                  ELSE
                    nap$store_client_identity (received_connection^.connection_id, client_identity,
                          {ignore} status);
                    status.normal := TRUE;
                    nap$offer_connection_switch (received_connection^.network_file_name, job, 0, status);
                    IF status.normal THEN
                      jmp$signal_pair_connect_target (job, status);
                      IF status.normal THEN
                        received_connection^.state := am_connection_switch_offered;
                      ELSE
                        nap$cancel_switch_offer (received_connection^.network_file_name, {ignore} status);
                        received_connection^.state := am_signal_failed;
                        status.normal := TRUE;
                      IFEND;
                    ELSE
                      received_connection^.state := am_switch_failed;
                    IFEND;
                    received_connection := received_connection^.next_entry;
                  IFEND;
                ELSE
                  received_connection^.state := am_validation_failed;
                  received_connection := received_connection^.next_entry;
                IFEND;
              ELSE
                received_connection^.state := am_server_inactive;
                received_connection := received_connection^.next_entry;
              IFEND;
              nlp$release_exclusive_access (server_attributes^.access_control);
            ELSE
              received_connection^.state := am_server_inactive;
              received_connection := received_connection^.next_entry;
            IFEND;
            nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);

          = am_connection_switch_offered =
            wait_list [1].activity := nac$i_await_switch_accept;
            wait_list [1].connection_file := ^received_connection^.network_file_name;
            wait_list [2].activity := osc$i_await_time;
            wait_list [2].milliseconds := 0;
            osp$i_await_activity_completion (wait_list, ready_index, status);
            IF status.normal AND (ready_index = 1) THEN
              amp$return (received_connection^.network_file_name, {ignore} status);
              status.normal := TRUE;
              previous_received_connection^ := received_connection^.next_entry;
              next_received_connection := received_connection^.next_entry;
              FREE received_connection IN osv$task_shared_heap^;
              received_connection := next_received_connection;
            ELSE
              nap$get_attributes (received_connection^.network_file_name, connection_attribute, status);
              IF NOT status.normal {AND (status.condition = nae$connection_terminated)} OR
                    (status.normal AND (connection_attribute [1].connection_state = nac$terminated)) OR
                    ((received_connection^.activity_stamp + ten_minutes) < #FREE_RUNNING_CLOCK (0)) THEN
                amp$return (received_connection^.network_file_name, {ignore} status);
                nap$cancel_switch_offer (received_connection^.network_file_name, {ignore} status);
                status.normal := TRUE;
                previous_received_connection^ := received_connection^.next_entry;
                next_received_connection := received_connection^.next_entry;
                FREE received_connection IN osv$task_shared_heap^;
                received_connection := next_received_connection;
              ELSE
                received_connection := received_connection^.next_entry;
              IFEND;
            IFEND;

          = am_server_inactive, am_validation_failed, am_switch_failed, am_signal_failed =
            nap$get_attributes (received_connection^.network_file_name, connection_attribute, status);
            IF NOT status.normal {AND (status.condition = nae$connection_terminated)} OR
                  (status.normal AND (connection_attribute [1].connection_state = nac$terminated)) OR
                  ((received_connection^.activity_stamp + ten_minutes) < #FREE_RUNNING_CLOCK (0)) THEN
              amp$return (received_connection^.network_file_name, {ignore} status);
              previous_received_connection^ := received_connection^.next_entry;
              next_received_connection := received_connection^.next_entry;
              FREE received_connection IN osv$task_shared_heap^;
              received_connection := next_received_connection;
            ELSE
              received_connection := received_connection^.next_entry;
            IFEND;
          ELSE
          CASEND;
        ELSE
        CASEND;

{ Queue the new connections at the end of the connections_in_dialog list.

        IF received_connection = NIL THEN
          previous_received_connection^ := new_connections_list;
          received_connection := new_connections_list;
          new_connections_list := NIL;
        IFEND;

      WHILEND /process_connection/;

      pmp$wait (wait_time, wait_time);
    WHILEND;

  PROCEND nap$am_poll_connections;
?? OLDTITLE ??
?? NEWTITLE := 'assign_connect_for_leveled_job', EJECT ??

{ PURPOSE:
{   The purpose of this request is to initialize the NAM/VE structures necessary for
{   a leveled interactive job.

  PROCEDURE assign_connect_for_leveled_job
    (    received_connection: ^am_received_connection_attr;
         server_attributes: ^nat$server_attributes,
         job_name: jmt$system_supplied_name);

    VAR
      assigned_connection: ^^nat$server_connection_attribute,
      ignore_status: ost$status,
      new_assigned_connection: ^nat$server_connection_attribute,
      previous_server_connection: ^^nat$server_connection_attribute,
      previous_wait_for_connection: ^^nat$wait_for_connection,
      server_connection: ^nat$server_connection_attribute,
      server_job_attributes: ^nat$server_job_attributes,
      wait_for_connection: ^nat$wait_for_connection;


{ Add server job attributes entry.

    REPEAT
      ALLOCATE server_job_attributes IN nav$network_paged_heap^;
      IF server_job_attributes = NIL THEN
        syp$cycle;
      IFEND;
    UNTIL server_job_attributes <> NIL;

    server_job_attributes^.job_name := job_name;
    server_job_attributes^.job_status := nac$server_job_initiated;
    server_job_attributes^.time_stamp := #FREE_RUNNING_CLOCK (0);
    server_job_attributes^.max_connections_per_server_job := 0;
    server_job_attributes^.connection_count := 0;
    IF (server_attributes^.server_job_validation_source = nac$client) THEN
      server_job_attributes^.assigned_connection_count := 1;
    ELSE
      server_job_attributes^.assigned_connection_count := 0;
    IFEND;

{ Link the server_job_attributes to the server_job_list.

    server_job_attributes^.next_entry := server_attributes^.server_job_list;
    server_attributes^.server_job_list := server_job_attributes;
    server_attributes^.server_job_init_pending := FALSE;

    nap$remove_connection_id (received_connection^.network_file_name, ignore_status);
    server_job_attributes^.assigned_connection_count := server_job_attributes^.assigned_connection_count + 1;

    server_connection := server_attributes^.server_connections_list;
    previous_server_connection := ^server_attributes^.server_connections_list;
    WHILE (server_connection <> NIL) AND (server_connection^.connection_id <>
          received_connection^.connection_id) DO
      previous_server_connection := ^server_connection^.next_entry;
      server_connection := server_connection^.next_entry;
    WHILEND;
    IF server_connection <> NIL THEN

      previous_server_connection^ := server_connection^.next_entry;
      new_assigned_connection := server_connection;
    ELSE

{ Connection disconnected and has been removed from list.

      RETURN; {----->
    IFEND;

    new_assigned_connection^.connection_kind := nac$assigned_to_job;
    new_assigned_connection^.directed_connection := TRUE;
    new_assigned_connection^.destination_job_name := job_name;
    new_assigned_connection^.terminate_connection := FALSE;
    new_assigned_connection^.time_stamp := #FREE_RUNNING_CLOCK (0);
    new_assigned_connection^.next_entry := NIL;
    assigned_connection := ^server_attributes^.assigned_connections_list;

{ Queue the new connection at the end of the assigned connections list.

    WHILE assigned_connection^ <> NIL DO
      assigned_connection := ^assigned_connection^^.next_entry;
    WHILEND;
    assigned_connection^ := new_assigned_connection;

{ Ready a waiting task.

    wait_for_connection := server_attributes^.wait_for_connection;
    previous_wait_for_connection := ^server_attributes^.wait_for_connection;

    WHILE (wait_for_connection <> NIL) AND (wait_for_connection^.job_name <> job_name) DO
      previous_wait_for_connection := ^wait_for_connection^.next_entry;
      wait_for_connection := wait_for_connection^.next_entry;
    WHILEND;

    IF wait_for_connection <> NIL THEN

      { Found the waiting_job.

      previous_wait_for_connection^ := wait_for_connection^.next_entry;
      pmp$ready_task (wait_for_connection^.task_id, ignore_status);
      FREE wait_for_connection IN nav$network_paged_heap^;
    IFEND;
  PROCEND assign_connect_for_leveled_job;
?? OLDTITLE ??
?? NEWTITLE := 'initiate_new_dialogs', EJECT ??

  PROCEDURE initiate_new_dialogs
    (VAR new_connections_list: ^am_received_connection_attr);

{ The purpose of this procedure is to acquire new connections and initiate
{ client validation. The client validation is nominally obtained via a dialog
{ with an interactive user.
{
{ However, the client validation may have been obtained on a another mainframe
{ in a cluster environment and is being passed to this mainframe via the remote
{ attach job protocol in the peer accounting and connect data. If the remote
{ attach job syntax is present, it is passed to job management for validation,
{ assuming validity the connection will be offered to the job which is the object
{ of the remote attach_job.

    CONST
      fixed_user_data_length = 114,
      paired_connection = 129,
      supported_accounting_version = 1,
      supported_user_data_version = 1,
      vtp_version_length = 2;

    TYPE
      accounting_header = record
        version: 0 .. 0ffff(16),
        caller_identifer: 0 .. 0ff(16),
      recend,

      fixed_user_data_record = record
        version: 0 .. 0ff(16),
        pad: array [1 .. (fixed_user_data_length - 1)] of 0 .. 0ff(16),
      recend;

    VAR
      access_creation_selections: [STATIC, READ, oss$job_paged_literal] array [1 .. 1] of
            fst$file_cycle_attribute := [[fsc$file_contents_and_processor, amc$list, osc$null_name]],
      access_selections: [STATIC, READ, oss$job_paged_literal] array
            [1 .. 1] of fst$attachment_option := [[fsc$access_and_share_modes,
            [fsc$specific_access_modes, [fsc$read, fsc$append, fsc$modify]], [fsc$required_share_modes]]],
      terminal_attributes: [STATIC, READ, oss$job_paged_literal] array [1 .. 7] of
            ift$connection_attribute := [[ifc$input_editing_mode, ifc$normal_edit], [ifc$input_timeout, TRUE],
            [ifc$input_timeout_length, 0], [ifc$input_timeout_purge, TRUE],
            [ifc$partial_char_forwarding, FALSE], [ifc$prompt_string, [1, ',']], * ];

    VAR
      accounting: ^accounting_header,
      actual_access_control: am_received_connections_list,
      byte_address: amt$file_byte_address,
      connection_attributes: array [1 .. 1] of nat$get_attribute,
      cs_status: osc$cs_successful .. osc$cs_variable_locked,
      file_id: amt$file_identifier,
      fixed_user_data: ^fixed_user_data_record,
      interactive_tip_data_length: nat$data_length,
      interactive_tip_data: SEQ (REP 512 of cell),
      network_file_name: ost$unique_name,
      new_access_control: am_received_connections_list,
      original_access_control: am_received_connections_list,
      peer_accounting: ^SEQ (REP jmc$job_input_device_size of cell),
      peer_connect_data: ^SEQ (REP 512 of cell),
      previous_received_connection: ^^am_received_connection_attr,
      received_connection: ^am_received_connection_attr,
      received_peer_accounting: ^SEQ ( * ),
      received_peer_connect_data: ^SEQ ( * ),
      retry_limit_reached: boolean,
      service_data_ii: ^string ( * ),
      service_data_length: ^0 .. 0ff(16),
      service_data: ^string ( * ),
      status: ost$status,
      terminal_file_name: ost$unique_name,
      term_conn_attributes: array [1 .. 7] of ift$connection_attribute;


{ Setup the selectable prompts to be included in the login dialog.

    include_prompt [nac$am_family_name] := nav$prompt_for_family_name;
    include_prompt [nac$am_account_name] := nav$prompt_for_account;
    include_prompt [nac$am_project_name] := nav$prompt_for_project;
    final_login_prompt := nav$final_login_prompt;

{ Extract the list of new connections.

    new_access_control.fill := 0;
    new_access_control.received_connection := NIL;
    original_access_control := new_access_control;
    REPEAT
      #COMPARE_SWAP (nav$received_connections_list, original_access_control, new_access_control,
            actual_access_control, cs_status);
      IF cs_status = osc$cs_failed THEN
        original_access_control := actual_access_control;
      IFEND;
    UNTIL (cs_status = osc$cs_successful);

    received_connection := original_access_control.received_connection;
    previous_received_connection := ^original_access_control.received_connection;
    connection_attributes [1].kind := nac$peer_accounting_information;

    WHILE (received_connection <> NIL) DO
      pmp$generate_unique_name (network_file_name, {ignore} status);
      nap$create_network_file (network_file_name.value, NIL, received_connection^.connection_id, FALSE,
            status);
      IF status.normal THEN
        received_connection^.network_file_name := network_file_name.value;
        connection_attributes [1].peer_accounting_information := ^received_connection^.peer_accounting_info;
        nap$get_attributes (network_file_name.value, connection_attributes, status);
      IFEND;
      IF NOT status.normal THEN
        nap$display_message (status);
        previous_received_connection^ := received_connection^.next_entry;
        decrement_received_conn_count (received_connection^.server, received_connection^.connection_id);
        FREE received_connection IN osv$task_shared_heap^;
        received_connection := previous_received_connection^;
      ELSE
        received_connection^.peer_accounting_info_length :=
              connection_attributes [1].peer_accounting_info_length;
        IF received_connection^.server = osc$timesharing THEN

{ Obtain the remote attach job protocol if present.

          peer_accounting := ^received_connection^.peer_accounting_info;
          RESET peer_accounting;
          NEXT received_peer_accounting: [[REP received_connection^.peer_accounting_info_length OF cell]] IN
                peer_accounting;
          IF received_peer_accounting <> NIL THEN
            RESET received_peer_accounting;
            NEXT accounting IN received_peer_accounting;
            IF (accounting <> NIL) AND ((accounting^.version = supported_accounting_version) AND
                  (accounting^.caller_identifer = paired_connection)) THEN
              received_connection^.prompt_user := FALSE;
              received_connection^.client_validation_kind := am_remote_attach_job;
              received_connection^.state := am_validation_failed;
              received_connection^.service_data_ii_length := 0;
              connection_attributes [1].kind := nac$peer_connect_data;
              connection_attributes [1].peer_connect_data := ^interactive_tip_data;
              nap$get_attributes (network_file_name.value, connection_attributes, status);
              IF status.normal THEN
                peer_connect_data := ^interactive_tip_data;
                interactive_tip_data_length := connection_attributes [1].peer_connect_data_length;
                RESET peer_connect_data;
                NEXT received_peer_connect_data: [[REP interactive_tip_data_length OF cell]] IN
                      peer_connect_data;
                RESET received_peer_connect_data;
                NEXT fixed_user_data IN received_peer_connect_data;
                IF (fixed_user_data <> NIL) AND (fixed_user_data^.version = supported_user_data_version) THEN
                  NEXT service_data_length IN received_peer_connect_data;
                  IF service_data_length <> NIL THEN
                    NEXT service_data: [service_data_length^ +vtp_version_length] IN
                          received_peer_connect_data;
                    IF service_data <> NIL THEN
                      NEXT service_data_length IN received_peer_connect_data;
                      IF (service_data_length <> NIL) AND (service_data_length^ > 0) THEN
                        NEXT service_data_ii: [service_data_length^] IN received_peer_connect_data;
                        IF service_data_ii <> NIL THEN
                          received_connection^.state := am_validate_job;
                          received_connection^.service_data_ii_length := service_data_length^;
                          received_connection^.service_data_ii (1, service_data_length^) :=
                                service_data_ii^ (1, service_data_length^);
                        IFEND;
                      IFEND;
                    IFEND;
                  IFEND;
                IFEND;
              IFEND;
            IFEND;
          IFEND;
        IFEND;

        IF received_connection^.client_validation_kind = am_client_dialog THEN
          pmp$generate_unique_name (terminal_file_name, {ignore} status);
          term_conn_attributes := terminal_attributes;
          term_conn_attributes [UPPERBOUND (term_conn_attributes)].key := ifc$prompt_file;
          term_conn_attributes [UPPERBOUND (term_conn_attributes)].prompt_file := terminal_file_name.value;

          rmp$request_terminal (terminal_file_name.value, ^network_file_name.value, term_conn_attributes,
                status);
          IF status.normal THEN
            received_connection^.terminal_file_name := terminal_file_name.value;
            fsp$open_file (terminal_file_name.value, amc$record, ^access_selections,
                  ^access_creation_selections, ^access_creation_selections, NIL, NIL, file_id, status);
            IF status.normal THEN
              received_connection^.file_id := file_id;
              amp$put_next (received_connection^.file_id, formatted_prompt [nac$am_login_banner],
                    STRLENGTH (formatted_prompt [nac$am_login_banner]^), byte_address, status);
              IF status.normal THEN
                received_connection^.last_prompt := nac$am_login_banner;
                received_connection^.prompt_user := TRUE;
                received_connection^.send_previous_prompt := FALSE;
              IFEND;
            ELSE

{ *** DEBUG pmp$log ('AM - open network file error', ignore_status);

            IFEND;
          ELSE

{ *** DEBUG pmp$log ('AM - req terminal error.', ignore_status);

          IFEND;

          IF NOT status.normal THEN

{ **** TEMP FOR DEBUG ONLY

            nap$display_message (status);

            previous_received_connection^ := received_connection^.next_entry;
            fsp$close_file (received_connection^.file_id, {ignore} status);
            amp$return (received_connection^.network_file_name, {ignore} status);
            amp$return (received_connection^.terminal_file_name, {ignore} status);
            decrement_received_conn_count (received_connection^.server, received_connection^.connection_id);
            FREE received_connection IN osv$task_shared_heap^;
            received_connection := previous_received_connection^;
          ELSE
            received_connection := received_connection^.next_entry;
          IFEND;
        ELSE
          received_connection := received_connection^.next_entry;
        IFEND;
      IFEND;
    WHILEND;

    new_connections_list := original_access_control.received_connection;

  PROCEND initiate_new_dialogs;
?? OLDTITLE ??
?? NEWTITLE := 'assign_connection', EJECT ??

  PROCEDURE [INLINE] assign_connection
    (    server_attributes: ^nat$server_attributes;
         client_validated_connection: boolean;
         connection_id: nat$connection_id;
         directed_connection: boolean;
         job_name: jmt$system_supplied_name);


    VAR
      assigned_connection: ^^nat$server_connection_attribute,
      ignore_status: ost$status,
      new_assigned_connection: ^nat$server_connection_attribute,
      previous_wait_for_connection: ^^nat$wait_for_connection,
      previous_server_connection: ^^nat$server_connection_attribute,
      server_connection: ^nat$server_connection_attribute,
      wait_for_connection: ^nat$wait_for_connection;

{ It is assumed that the server attributes entry has been locked for exclusive
{ access by the caller.

    IF client_validated_connection THEN

{ The connection had been acquired by the server to get the validation info.
{ Search the server connections list for the connection and move it to the
{ assigned connections list.

      server_connection := server_attributes^.server_connections_list;
      previous_server_connection := ^server_attributes^.server_connections_list;
      WHILE (server_connection <> NIL) AND (server_connection^.connection_id <> connection_id) DO
        previous_server_connection := ^server_connection^.next_entry;
        server_connection := server_connection^.next_entry;
      WHILEND;
      IF server_connection <> NIL THEN
        previous_server_connection^ := server_connection^.next_entry;
        new_assigned_connection := server_connection;
      ELSE

{ Connection disconnected and has been removed from list.

        RETURN; {----->
      IFEND;
    ELSE
      REPEAT
        ALLOCATE new_assigned_connection IN nav$network_paged_heap^;
        IF new_assigned_connection = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL new_assigned_connection <> NIL;
      new_assigned_connection^.connection_id := connection_id;
    IFEND;

    new_assigned_connection^.connection_kind := nac$assigned_to_job;
    new_assigned_connection^.directed_connection := directed_connection;
    IF directed_connection THEN
      new_assigned_connection^.destination_job_name := job_name;
    IFEND;

    new_assigned_connection^.terminate_connection := FALSE;
    new_assigned_connection^.time_stamp := #FREE_RUNNING_CLOCK (0);
    new_assigned_connection^.next_entry := NIL;
    assigned_connection := ^server_attributes^.assigned_connections_list;

{ Queue the new connection at the end of the assigned connections list.

    WHILE assigned_connection^ <> NIL DO
      assigned_connection := ^assigned_connection^^.next_entry;
    WHILEND;
    assigned_connection^ := new_assigned_connection;

{  Ready a waiting task.

    wait_for_connection := server_attributes^.wait_for_connection;
    previous_wait_for_connection := ^server_attributes^.wait_for_connection;

    WHILE (wait_for_connection <> NIL) AND (directed_connection) AND
          (wait_for_connection^.job_name <> job_name) DO
      previous_wait_for_connection := ^wait_for_connection^.next_entry;
      wait_for_connection := wait_for_connection^.next_entry;
    WHILEND;

    IF wait_for_connection <> NIL THEN

{ Found the waiting job.

      previous_wait_for_connection^ := wait_for_connection^.next_entry;
      pmp$ready_task (wait_for_connection^.task_id, ignore_status);
      FREE wait_for_connection IN nav$network_paged_heap^;
    IFEND;

  PROCEND assign_connection;
?? OLDTITLE ??
?? NEWTITLE := 'assign_received_connection', EJECT ??

  PROCEDURE [INLINE] assign_received_connection
    (    server_attributes: ^nat$server_attributes;
         connection_id: nat$connection_id;
     VAR status: ost$status);

{ The purpose of this procedure is to assign the received connection for which
{ no validation is required. The connection is assigned either to an existing
{ job that can acquire more connections or a new job is initiated to acquire
{ the received connection. The connection would be rejected in case it is
{ destined for an application initiated server and the currently signed on jobs
{ are unable to acquire more connections.


    VAR
      server_job_attributes: ^nat$server_job_attributes,
      server_capacity: integer,
      assigned_connection: ^nat$server_connection_attribute,
      job_name: jmt$system_supplied_name,
      login: am_login_parameters;

{ This proc assumes that the server attributes has been locked for exclusive
{ access by the caller.

    status.normal := TRUE;
    server_capacity := 0;
    server_job_attributes := server_attributes^.server_job_list;

    WHILE server_job_attributes <> NIL DO
      IF server_job_attributes^.job_status = nac$server_job_initiated THEN
        server_capacity := server_capacity + server_attributes^.server_job_max_connections;
      ELSE
        IF server_job_attributes^.job_status = nac$server_job_attached THEN
          server_capacity := (server_job_attributes^.max_connections_per_server_job -
                server_job_attributes^.connection_count) + server_capacity;
        IFEND;
      IFEND;

{ NOTE: Deactivated jobs capacity is not included.

      server_job_attributes := server_job_attributes^.next_entry;
    WHILEND;

{  Sum up the assigned connections.

    assigned_connection := server_attributes^.assigned_connections_list;

    WHILE assigned_connection <> NIL DO
      server_capacity := server_capacity - 1;
      assigned_connection := assigned_connection^.next_entry;
    WHILEND;

    IF (NOT server_attributes^.nam_initiated_server) AND (server_capacity <= 0) THEN
      IF server_attributes^.server_job_list <> NIL THEN
        osp$set_status_abnormal (nac$status_id, nae$insufficient_attached_jobs, server_attributes^.server,
              status);
      ELSE { server_attributes^.server_job_list = NIL
        osp$set_status_abnormal (nac$status_id, nae$no_server_job_active, server_attributes^.server, status);
      IFEND;
      nap$display_message (status);
    ELSE
      IF server_capacity <= 0 THEN
        initiate_saj (server_attributes, login, NIL, 0, NIL, job_name, status);
      IFEND;

      IF status.normal THEN
        assign_connection (server_attributes, FALSE, connection_id, FALSE, jmc$full_system_supplied_name);
      IFEND;
    IFEND;

  PROCEND assign_received_connection;
?? OLDTITLE ??
?? NEWTITLE := 'assign_validated_connection', EJECT ??

  PROCEDURE assign_validated_connection
    (    received_connection: ^am_received_connection_attr;
         transfer_count: amt$transfer_count;
         login_params: ^SEQ ( * );
         login_specification: am_login_specification;
     VAR status: ost$status);

{ The purpose of this procedure is to assign a client validated connection. A
{ client validated connection is one for which FAMILY, USER_NAME AND PASSWORD
{ have been requested from the client that initiated the connection. If the connection
{ is destined for a server that provides login, prevalidation of the requested
{ parameters is done and the connection is assigned to the server jobs. But if
{ login is to be provided by the client, prevalidation is done only if the
{ connection can be acquired by an existing server job.


    VAR
      byte_address: amt$file_byte_address,
      client_identity: nat$client_identity,
      default_job_attributes_p: ^jmt$default_attribute_results,
      ignore_status: ost$status,
      job_name: jmt$system_supplied_name,
      login_parameters: ^SEQ ( * ),
      login_password: ost$name,
      login_string: ^string ( * ),
      login_string_size: ^clt$command_line_size,
      parameter_list: ^clt$parameter_list,
      server_attributes: ^nat$server_attributes,
      server_job_attributes: ^nat$server_job_attributes,
      validation_attributes: ^avt$validation_items;


    status.normal := TRUE;
    login_parameters := login_params;
    IF login_specification = am_via_string THEN
      IF received_connection^.login_string = NIL THEN
        RESET login_parameters;
        NEXT login_string_size IN login_parameters;
        login_string_size^ := transfer_count;
        NEXT login_string: [transfer_count] IN login_parameters;
      ELSE
        login_parameters := received_connection^.login_string;
        RESET login_parameters;
        NEXT login_string_size IN login_parameters;
        NEXT login_string: [login_string_size^] IN login_parameters;
      IFEND;
    ELSE
      login_string := NIL;
    IFEND;

    nlp$get_nonexclusive_access (nav$server_attributes_list.access_control);
    nap$find_server_attributes (received_connection^.server, server_attributes);
    IF server_attributes = NIL THEN
      osp$set_status_condition (nae$unknown_application, status);
    ELSE
      nlp$get_exclusive_access (server_attributes^.access_control);
      IF server_attributes^.server_status = nac$application_inactive THEN
        osp$set_status_condition (nae$application_inactive, status);
      ELSE
        server_job_attributes := server_attributes^.server_job_list;

      /find_saj/
        WHILE server_job_attributes <> NIL DO
          IF (server_job_attributes^.connection_count + server_job_attributes^.assigned_connection_count) <
                server_job_attributes^.max_connections_per_server_job THEN
            EXIT /find_saj/; {----->
          IFEND;

          server_job_attributes := server_job_attributes^.next_entry;
        WHILEND /find_saj/;


        IF server_job_attributes <> NIL THEN
          IF login_specification = am_via_string THEN
            RESET login_parameters;
            NEXT parameter_list: [[REP (#SIZE (login_string_size^) + login_string_size^) OF cell]] IN
                  login_parameters;
            clp$push_parameters (ignore_status);

{ The data returned by this request will be the value supplied by the user or a
{ null name (osc$null_name).

            clp$get_login_data_for_nam (parameter_list^, received_connection^.login_parameters.user_name,
                  login_password, received_connection^.login_parameters.family_name,
                  received_connection^.login_parameters.account_name,
                  received_connection^.login_parameters.project_name, status);
            clp$pop_parameters (ignore_status);
            IF NOT status.normal THEN
              nlp$release_exclusive_access (server_attributes^.access_control);
              nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);
              RETURN; {----->
            IFEND;
            IF received_connection^.login_parameters.password = osc$null_name THEN
              received_connection^.login_parameters.password := login_password;
            IFEND;
          IFEND;

{ Get default family name, if not specified.

          IF received_connection^.login_parameters.family_name = osc$null_name THEN
            PUSH default_job_attributes_p: [1 .. 1];
            default_job_attributes_p^ [1].key := jmc$login_family;
            jmp$get_attribute_defaults (jmc$batch, default_job_attributes_p, {ignore} status);
            received_connection^.login_parameters.family_name := default_job_attributes_p^ [1].login_family;
          IFEND;

{ Prevalidate job.  The validation attributes should be in the order that you want them validated.

{*** DEBUG pmp$log_ascii ('NAM prevalidate_user', $pmt$ascii_logset [pmc$system_log],
{*** DEBUG pmc$msg_origin_system, ignore_status);
{*** DEBUG pmp$log_ascii (server_attributes^.client_validation_capability, $pmt$ascii_logset [pmc$system_log],
{*** DEBUG pmc$msg_origin_system, ignore_status);

          PUSH validation_attributes: [1 .. 3];
          validation_attributes^ [1].key := avc$password_key;
          validation_attributes^ [1].password := received_connection^.login_parameters.password;
          validation_attributes^ [2].key := avc$account_project_key;
          validation_attributes^ [2].account_name := received_connection^.login_parameters.account_name;
          validation_attributes^ [2].project_name := received_connection^.login_parameters.project_name;
          IF server_attributes^.client_validation_capability <> osc$null_name THEN
            validation_attributes^ [3].key := avc$required_capability_key;
            validation_attributes^ [3].required_capability := server_attributes^.client_validation_capability;
          ELSE
            validation_attributes^ [3].key := avc$null_validation_key;
          IFEND;

          job_name := server_job_attributes^.job_name;

{ *** DEBUG pmp$log ('AM PREVALIDATE USER', ignore_status);

          nlp$release_exclusive_access (server_attributes^.access_control);
          avp$prevalidate_job (received_connection^.login_parameters.user_name,
                received_connection^.login_parameters.family_name, validation_attributes, NIL, status);
          nlp$get_exclusive_access (server_attributes^.access_control);
        ELSE
          IF server_attributes^.nam_initiated_server THEN
            initiate_saj (server_attributes, received_connection^.login_parameters, login_string,
                  received_connection^.peer_accounting_info_length,
                  ^received_connection^.peer_accounting_info, job_name, status);
          ELSE

{ Should not end up here.

            osp$set_status_abnormal (nac$status_id, nae$insufficient_attached_jobs,
                  received_connection^.server, status);
            nap$display_message (status);
          IFEND;
        IFEND;

        IF status.normal THEN
          nap$remove_connection_id (received_connection^.network_file_name, {ignore} status);
          status.normal := TRUE;
          IF server_job_attributes <> NIL THEN
            server_job_attributes^.assigned_connection_count :=
                  server_job_attributes^.assigned_connection_count + 1;
          IFEND;
          assign_connection (server_attributes, TRUE, received_connection^.connection_id, TRUE, job_name);

        IFEND;
      IFEND;
      nlp$release_exclusive_access (server_attributes^.access_control);
    IFEND;

    nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);

    IF status.normal THEN
      client_identity.family := received_connection^.login_parameters.family_name;
      client_identity.user := received_connection^.login_parameters.user_name;
      nap$store_client_identity (received_connection^.connection_id, client_identity, {ignore} status);
      status.normal := TRUE;
    IFEND;

  PROCEND assign_validated_connection;
?? OLDTITLE ??
?? NEWTITLE := '[inline] decrement_received_conn_count', EJECT ??

  PROCEDURE [INLINE] decrement_received_conn_count
    (    server: nat$application_name;
         connection_id: nat$connection_id);

    VAR
      previous_server_connection: ^^nat$server_connection_attribute,
      server_attributes: ^nat$server_attributes,
      server_connection: ^nat$server_connection_attribute;

    nlp$get_nonexclusive_access (nav$server_attributes_list.access_control);
    nap$find_server_attributes (server, server_attributes);
    IF server_attributes <> NIL THEN
      nlp$get_exclusive_access (server_attributes^.access_control);
      server_connection := server_attributes^.server_connections_list;
      previous_server_connection := ^server_attributes^.server_connections_list;
      WHILE (server_connection <> NIL) AND (server_connection^.connection_id <> connection_id) DO
        previous_server_connection := ^server_connection^.next_entry;
        server_connection := server_connection^.next_entry;
      WHILEND;
      IF server_connection <> NIL THEN
        previous_server_connection^ := server_connection^.next_entry;
        FREE server_connection IN nav$network_paged_heap^;
        server_attributes^.connection_count := server_attributes^.connection_count - 1;
      IFEND;
      nlp$release_exclusive_access (server_attributes^.access_control);
    IFEND;
    nlp$release_nonexclusive_access (nav$server_attributes_list.access_control);

  PROCEND decrement_received_conn_count;
?? OLDTITLE ??
?? NEWTITLE := 'extract_login_parameters', EJECT ??

  PROCEDURE extract_login_parameters
    (    received_connection: ^am_received_connection_attr;
         transfer_count: amt$transfer_count;
         login_params: ^SEQ ( * );
     VAR login_specification: am_login_specification;
     VAR re_prompt: boolean);

    VAR
      account_name: avt$account_name,
      byte_address: amt$file_byte_address,
      family_name: ost$family_name,
      ignore_status,
      local_status: ost$status,
      login_parameters: ^SEQ ( * ),
      parameter_list: ^clt$parameter_list,
      parameter_list_size: ^clt$command_line_size,
      parameter_name: ost$name,
      parameters: ^string ( * ),
      password: ost$name,
      project_name: avt$project_name,
      prompt: nat$am_login_prompt,
      prompt_for_password: boolean,
      user_name: ost$user_name,
      valid_name: boolean;

    login_specification := am_incomplete;
    re_prompt := FALSE;
    local_status.normal := TRUE;

    login_parameters := login_params;
    RESET login_parameters;
    NEXT parameter_list_size IN login_parameters;
    parameter_list_size^ := transfer_count;
    NEXT parameters: [transfer_count] IN login_parameters;

    IF transfer_count <= osc$max_name_size THEN
      clp$validate_name (parameters^, parameter_name, valid_name);
    ELSE
      valid_name := FALSE;
    IFEND;

    CASE received_connection^.last_prompt OF
    = nac$am_user_name =
      IF received_connection^.login_string <> NIL THEN
        FREE received_connection^.login_string IN osv$task_shared_heap^;
      IFEND;
      IF valid_name THEN
        received_connection^.login_parameters.user_name := parameter_name;
        amp$put_next (received_connection^.file_id, NIL, 0, byte_address, ignore_status);
      ELSE
        overwrite_login_info (received_connection^.file_id, transfer_count, nac$am_user_name, blackout,
              {suppress_cursor_positioning} TRUE);
        prompt_for_password := nav$force_password_prompt;
        IF NOT prompt_for_password THEN {verify password was specified in string}
          RESET login_parameters;
          NEXT parameter_list: [[REP (#SIZE (parameter_list_size^) + transfer_count) OF cell]] IN
                login_parameters;
          clp$push_parameters (ignore_status);

{ The data returned by this request will be the value supplied by the user or a null name (osc$null_name).

          clp$get_login_data_for_nam (parameter_list^, user_name, password, family_name, account_name,
                project_name, ignore_status);
          clp$pop_parameters (ignore_status);
          prompt_for_password := password = osc$null_name;
        IFEND;
        IF prompt_for_password THEN
          ALLOCATE received_connection^.login_string: [[REP (transfer_count + #SIZE (parameter_list_size^)) OF
                cell]] IN osv$task_shared_heap^;
          i#move (^login_parameters^, ^received_connection^.login_string^,
                #SIZE (received_connection^.login_string^));
        ELSE {No further prompting necessary.}
          login_specification := am_via_string;
        IFEND;
      IFEND;
    = nac$am_password =
      overwrite_login_info (received_connection^.file_id, transfer_count, nac$am_password, blank_only,
            {suppress_cursor_positioning} TRUE);
      IF valid_name THEN
        received_connection^.login_parameters.password := parameter_name;
        IF received_connection^.login_string <> NIL THEN
          login_specification := am_via_string;
        ELSEIF final_login_prompt = nac$am_password THEN
          login_specification := am_via_parameters;
        IFEND;
      ELSE
        osp$set_status_condition (nae$improper_name_at_login, local_status);
      IFEND;
    = nac$am_family_name =
      IF valid_name THEN
        received_connection^.login_parameters.family_name := parameter_name;
        IF final_login_prompt = nac$am_family_name THEN
          login_specification := am_via_parameters;
        IFEND;
      ELSE
        osp$set_status_condition (nae$improper_name_at_login, local_status);
      IFEND;
    = nac$am_account_name =
      IF valid_name THEN
        received_connection^.login_parameters.account_name := parameter_name;
        IF final_login_prompt = nac$am_account_name THEN
          login_specification := am_via_parameters;
        IFEND;
      ELSE
        osp$set_status_condition (nae$improper_name_at_login, local_status);
      IFEND;
    = nac$am_project_name =
      IF valid_name THEN
        received_connection^.login_parameters.project_name := parameter_name;
        login_specification := am_via_parameters;
      ELSE
        osp$set_status_condition (nae$improper_name_at_login, local_status);
      IFEND;
    ELSE
    CASEND;

    IF NOT local_status.normal THEN
      osp$output_status_message (received_connection^.file_id, osc$brief_message_level,
            osc$subdued_status_message_hdr, local_status, ignore_status);
      IF received_connection^.login_parameters.retry_count_for_login_prompt <
            (nav$maximum_login_attempts - 1) THEN
        osp$set_status_condition (nae$retry_login, local_status);
        osp$output_status_message (received_connection^.file_id, osc$brief_message_level,
              osc$subdued_status_message_hdr, local_status, ignore_status);
      IFEND;
      re_prompt := TRUE;
    IFEND;

  PROCEND extract_login_parameters;
?? OLDTITLE ??
?? NEWTITLE := 'format_login_prompts', EJECT ??

  PROCEDURE format_login_prompts;

    VAR
      message: ost$status_message,
      prompt: nat$am_login_prompt,
      prompt_area: ^ost$status_message,
      prompt_line: ^string ( * ),
      prompt_line_count: ^ost$status_message_line_count,
      prompt_line_size: ^ost$status_message_line_size,
      status: ost$status;

    FOR prompt := nac$am_login_banner TO nac$am_project_name DO
      osp$format_multi_part_message (osc$brief_message_level, osc$subdued_status_message_hdr,
            osc$status_message_width, raw_prompt [prompt], NIL, NIL, message, status);
      IF NOT status.normal THEN
        nap$display_message (status);
        RETURN; {----->
      IFEND;

{ All prompts are assumed to be one line long.

      prompt_area := ^message;
      RESET prompt_area;
      NEXT prompt_line_count IN prompt_area;
      NEXT prompt_line_size IN prompt_area;
      NEXT prompt_line: [prompt_line_size^] IN prompt_area;
      ALLOCATE formatted_prompt [prompt]: [prompt_line_size^] IN osv$task_private_heap^;
      formatted_prompt [prompt]^ := prompt_line^;
    FOREND;
  PROCEND format_login_prompts;
?? OLDTITLE ??
?? NEWTITLE := 'get_server_attributes', EJECT ??

  PROCEDURE [INLINE] get_server_attributes
    (    sap: nat$sap_identifier;
     VAR server_attributes: ^nat$server_attributes);

{ This procedure assumes that the server attributes list  has been locked
{ by the caller.

    IF sap.kind = nac$osi_sap_identifier THEN
      server_attributes := nav$server_attributes_list.server_attributes;
      WHILE (server_attributes <> NIL) AND (server_attributes^.application_id.osi_sap_identifier <>
            sap.identifier) DO
        server_attributes := server_attributes^.next_entry;
      WHILEND;
    ELSE
      server_attributes := NIL;
    IFEND;

  PROCEND get_server_attributes;
?? OLDTITLE ??
?? NEWTITLE := 'initiate_saj', EJECT ??

  PROCEDURE initiate_saj
    (    server_attributes: ^nat$server_attributes;
         login: am_login_parameters;
         login_string: ^string ( * );
         peer_accounting_info_length: nat$data_length;
         peer_accounting_info: ^SEQ (REP jmc$job_input_device_size of cell);
     VAR job_name: jmt$system_supplied_name;
     VAR status: ost$status);

{ The purpose of this procedure is to initiate a server application job.
{ If the login parameters have been requested from the client, they would be
{ provided on the call to submit job routine. If login is provided by the
{ application definer, login would be the first statement on the file that
{ represents the service provided by the application.

    VAR
      highest_cycle: [STATIC, READ, oss$job_paged_literal] pft$cycle_selector := [pfc$highest_cycle],
      usage_selections: [STATIC, READ, oss$job_paged_literal] pft$usage_selections := [pfc$read],
      share_selections: [STATIC, READ, oss$job_paged_literal] pft$usage_selections := [pfc$read],
      path: [STATIC, READ, oss$job_paged_literal] array [1 .. 6] of pft$name :=
            [nac$application_family, nac$application_master_catalog, nac$network_subcatalog,
            nac$application_catalog, nac$application_job_catalog, * ];

    VAR
      default_job_attributes: array [1 .. 1] of jmt$default_attribute_result,
      server_job_attributes: ^nat$server_job_attributes,
      i: integer,
      ignore_status: ost$status,
      job_submission_options: array [1 .. max_job_submission_options] of jmt$job_submission_option,
      j: integer,
      file_name: ost$unique_name,
      path_name: array [1 .. 6] of pft$name,
      peer_accounting_information: ^string ( * ),
      peer_accounting_info_seq: ^SEQ (REP jmc$job_input_device_size of cell);


{ It is assumed that the server_attributes has been locked for exclusive access
{ by the caller.

    status.normal := TRUE;
    i := 1;


    IF server_attributes^.server_job_validation_source = nac$client THEN

      IF login_string <> NIL THEN
        job_submission_options [i].key := jmc$login_command;
        job_submission_options [i].login_command := login_string;
        i := i + 1;
        IF login.password <> osc$null_name THEN
          job_submission_options [i].key := jmc$login_password;
          job_submission_options [i].login_password := login.password;
          i := i + 1;
        IFEND;
      ELSE
        IF login.family_name <> osc$null_name THEN
          job_submission_options [i].key := jmc$login_family;
          job_submission_options [i].login_family := login.family_name;
          i := i + 1;
        IFEND;

        job_submission_options [i].key := jmc$login_password;
        job_submission_options [i].login_password := login.password;
        i := i + 1;

        job_submission_options [i].key := jmc$login_user;
        job_submission_options [i].login_user := login.user_name;
        i := i + 1;

        IF login.account_name <> osc$null_name THEN
          job_submission_options [i].key := jmc$login_account;
          job_submission_options [i].login_account := login.account_name;
          i := i + 1;
        IFEND;

        IF login.project_name <> osc$null_name THEN
          job_submission_options [i].key := jmc$login_project;
          job_submission_options [i].login_project := login.project_name;
          i := i + 1;
        IFEND;
      IFEND;

      job_submission_options [i].key := jmc$login_command_supplied;
      job_submission_options [i].login_command_supplied := FALSE;
      i := i + 1;

      IF peer_accounting_info_length > 0 THEN
        job_submission_options [i].key := jmc$job_input_device;
        PUSH job_submission_options [i].job_input_device;
        job_submission_options [i].job_input_device^.size := peer_accounting_info_length;
        peer_accounting_info_seq := peer_accounting_info;
        RESET peer_accounting_info_seq;
        NEXT peer_accounting_information: [peer_accounting_info_length] IN peer_accounting_info_seq;
        job_submission_options [i].job_input_device^.text := peer_accounting_information^;
        i := i + 1;
      IFEND;
    ELSE
      job_submission_options [i].key := jmc$login_command_supplied;
      job_submission_options [i].login_command_supplied := TRUE;
      i := i + 1;
    IFEND;

{ Fill in the common job submission attributes.

    job_submission_options [i].key := jmc$immediate_init_candidate;
    job_submission_options [i].immediate_init_candidate := TRUE;
    i := i + 1;

    IF server_attributes^.client_validation_capability <> osc$null_name THEN
      job_submission_options [i].key := jmc$required_user_capability;
      job_submission_options [i].required_user_capability := server_attributes^.client_validation_capability;
      i := i + 1;
    IFEND;

    job_submission_options [i].key := jmc$origin_application_name;
    job_submission_options [i].origin_application_name := server_attributes^.server;
    i := i + 1;

{ Get default family name.

    default_job_attributes [1].key := jmc$login_family;
    IF server_attributes^.server = osc$timesharing THEN
      jmp$get_attribute_defaults (jmc$interactive_connected, ^default_job_attributes, {ignore} status);
    ELSE
      jmp$get_attribute_defaults (jmc$batch, ^default_job_attributes, {ignore} status);
    IFEND;
    job_submission_options [i].key := jmc$default_login_family;
    job_submission_options [i].default_login_family := default_job_attributes [1].login_family;

{ Initialize unused slots to a null value.

    FOR j := (i + 1) TO UPPERBOUND (job_submission_options) DO
      job_submission_options [j].key := jmc$null_attribute;
    FOREND;

    IF NOT server_attributes^.service_file_defined THEN
      file_name.value := clc$null_file;
    ELSE
      pmp$generate_unique_name (file_name, ignore_status);
      path_name := path;
      path_name [UPPERBOUND (path_name)] := server_attributes^.server;
      pfp$attach (file_name.value, path_name, highest_cycle, osc$null_name, usage_selections,
            share_selections, pfc$no_wait, status);
      IF NOT status.normal THEN
        nap$display_message (status);
      IFEND;
    IFEND;

    IF status.normal THEN

{ *** DEBUG pmp$log ('AM SUBMITTING NAM INITIATED SAJ.', ignore_status);

      server_attributes^.server_job_init_pending := TRUE;

{ The server attributes is unlocked to avoid potential deadlocks.
{ The server attributes entry is marked to indicate that the server attributes
{ entry needs to be updated after submitting the job. The newly initiated job
{ will wait to attach to the server when this flag is set.

      nlp$release_exclusive_access (server_attributes^.access_control);
      jmp$submit_job (file_name.value, ^job_submission_options, job_name, status);

{ Note: Since the non exclusive lock to the server attributes list is not released, the
{ server cannot be deleted in the mean time. Hence no search is neccessary to find the
{ server attributes entry again.

      nlp$get_exclusive_access (server_attributes^.access_control);
      server_attributes^.server_job_init_pending := FALSE;
      IF file_name.value <> clc$null_file THEN
        amp$return (file_name.value, ignore_status);
      IFEND;

      IF status.normal THEN

{ Since the server attributes lock was released before calling jmp$submit_job we
{ need to verify the server status.

        IF server_attributes^.server_status = nac$application_inactive THEN
          osp$set_status_abnormal (nac$status_id, nae$application_inactive, server_attributes^.server,
                status);
        ELSE

{ Add server job attributes entry.

          REPEAT
            ALLOCATE server_job_attributes IN nav$network_paged_heap^;
            IF server_job_attributes = NIL THEN
              syp$cycle;
            IFEND;
          UNTIL server_job_attributes <> NIL;
          server_job_attributes^.job_name := job_name;
          server_job_attributes^.job_status := nac$server_job_initiated;
          server_job_attributes^.time_stamp := #FREE_RUNNING_CLOCK (0);
          server_job_attributes^.max_connections_per_server_job := 0;
          server_job_attributes^.connection_count := 0;

          IF (server_attributes^.server_job_validation_source = nac$client) THEN
            server_job_attributes^.assigned_connection_count := 1;
          ELSE
            server_job_attributes^.assigned_connection_count := 0;
          IFEND;

{ Link the server_job_attributes to the server_job_list.

          server_job_attributes^.next_entry := server_attributes^.server_job_list;
          server_attributes^.server_job_list := server_job_attributes;
        IFEND;
      IFEND;
    IFEND;

  PROCEND initiate_saj;
?? OLDTITLE ??
?? NEWTITLE := 'overwrite_login_info', EJECT ??

  PROCEDURE overwrite_login_info
    (    file_id: amt$file_identifier;
         user_input_length: clt$command_line_size;
         prompt: nat$am_login_prompt;
         overwrite_kind: (blank_only, blackout);
         suppress_cursor_positioning: boolean);

{ This procedure overwrites the user password at the terminal.
{ In order for the overwrite to work correctly, cursor positioning
{ must have been suppressed prior to the previous output. (i.e.,
{ cursor positioning must be suppressed before issuing the prompt)


    VAR
      byte_address: amt$file_byte_address,
      i: 0 .. 0ff(16),
      j: 0 .. 0ffff(16),
      ignore_status: ost$status,
      overwrite_length: clt$command_line_size;


    IF user_input_length < osc$max_name_size THEN
      overwrite_length := osc$max_name_size;
    ELSE
      overwrite_length := user_input_length;
    IFEND;

{ Change the format effector to a '+' so that no line feed is done prior to
{ writing the line.

    IF suppress_cursor_positioning THEN
      formatted_prompt [prompt]^ (1) := ifc$pre_print_start_of_line;
    IFEND;

    IF overwrite_kind = blackout THEN
      FOR i := 1 TO 3 DO
        amp$put_partial (file_id, formatted_prompt [prompt], STRLENGTH (formatted_prompt [prompt]^),
              byte_address, amc$start, ignore_status);
        FOR j := 1 TO (overwrite_length DIV overwrite_pattern_size) DO
          amp$put_partial (file_id, ^login_overwrite_pattern (i), overwrite_pattern_size, byte_address,
                amc$continue, ignore_status);
        FOREND;
        amp$put_partial (file_id, ^login_overwrite_pattern (i), overwrite_length MOD overwrite_pattern_size,
              byte_address, amc$terminate, ignore_status);
        formatted_prompt [prompt]^ (1) := ifc$pre_print_start_of_line;
      FOREND;
    IFEND;

{   Blank overwrite.

    formatted_prompt [prompt]^ (1) := ifc$pre_print_start_of_line;
    amp$put_partial (file_id, formatted_prompt [prompt], STRLENGTH (formatted_prompt [prompt]^), byte_address,
          amc$start, ignore_status);
    FOR j := 1 TO (overwrite_length DIV overwrite_pattern_size) DO
      amp$put_partial (file_id, ^login_blank_overwrite, overwrite_pattern_size, byte_address, amc$continue,
            ignore_status);
    FOREND;
    amp$put_partial (file_id, ^login_blank_overwrite, overwrite_length MOD overwrite_pattern_size,
          byte_address, amc$terminate, ignore_status);

{ Change format effector back to original value.

    formatted_prompt [prompt]^ (1) := ifc$pre_print_space_1;

  PROCEND overwrite_login_info;

?? OLDTITLE ??
?? NEWTITLE := '[INLINE] prompt_user', EJECT ??

  PROCEDURE [INLINE] prompt_user
    (    received_connection: ^am_received_connection_attr;
     VAR retry_limit_reached: boolean;
     VAR status: ost$status);

{ The purpose of this procedure is to set up the prompt string to be output by
{ the next call to amp$get_next. The prompt string prompts the client for the
{ required validation parameters. If SEND_PREVIOUS_PROMPT is true, then the
{ previous prompt string is set up for subsequent output to the client.


    VAR
      byte_address: amt$file_byte_address,
      prompt_control: char,
      terminal_attribute: array [1 .. 1] of ift$connection_attribute;

    status.normal := TRUE;
    retry_limit_reached := FALSE;
    IF received_connection^.send_previous_prompt THEN
      IF received_connection^.login_parameters.retry_count_for_login_prompt <
            (nav$maximum_login_attempts - 1) THEN
        received_connection^.login_parameters.retry_count_for_login_prompt :=
              received_connection^.login_parameters.retry_count_for_login_prompt + 1;
      ELSE
        retry_limit_reached := TRUE;
      IFEND;
    ELSE
      REPEAT
        received_connection^.last_prompt := SUCC (received_connection^.last_prompt);
      UNTIL include_prompt [received_connection^.last_prompt];
      received_connection^.login_parameters.retry_count_for_login_prompt := 0;
    IFEND;

    IF NOT retry_limit_reached THEN
      prompt_control := ifc$pre_print_space_1;
      IF received_connection^.last_prompt = nac$am_password THEN
        overwrite_login_info (received_connection^.file_id, osc$max_name_size, nac$am_password, blackout,
              {suppress_cursor_positioning} FALSE);
        ifp$suppress_cursor_pos_echoplx (suppress_cursor_positioning, suppress_echoplex);
        prompt_control := ifc$pre_print_start_of_line;
      ELSEIF received_connection^.last_prompt = nac$am_user_name THEN
        ifp$suppress_cursor_pos_echoplx (suppress_cursor_positioning, NOT suppress_echoplex);
      IFEND;
      terminal_attribute [1].key := ifc$prompt_string;
      terminal_attribute [1].prompt_string.size := STRLENGTH (formatted_prompt
            [received_connection^.last_prompt]^);
      terminal_attribute [1].prompt_string.value := formatted_prompt [received_connection^.last_prompt]^;
      terminal_attribute [1].prompt_string.value (1) := prompt_control;
      iip$direct_store_trm_conn_atts (received_connection^.file_id, terminal_attribute, status);
    IFEND;

  PROCEND prompt_user;
?? OLDTITLE ??
?? NEWTITLE := 'report_unsuccessful_login' ??
?? NEWTITLE := '  [INLINE] add_to_line', EJECT ??

{ The purpose of this procedure is to write a message to the system ascii log and to emit the
{ statistic nac$maximum_logins_exceeded because a user attempting to log in has exceeded the
{ maximum number of login attempts allowed.  The message and statistic contain the user name
{ and family name which the user supplied on the last invalid attempt, along with the terminal
{ name.  In addition, the statistic contains the maximum login attempts.

  PROCEDURE report_unsuccessful_login
    (    received_connection: ^am_received_connection_attr;
         login_params: ^SEQ ( * );
         login_specification: am_login_specification);

    CONST
      unknown = '???';

    CONST
      max_data_area_size = 1000;

    VAR
      account_name: avt$account_name,
      accounting_data: array [1 .. 1] of nat$accounting_data_field,
      connection_attribute: array [1 .. 1] of nat$get_attribute,
      default_job_attributes: array [1 .. 1] of jmt$default_attribute_result,
      family_name: ost$family_name,
      line: string (osc$max_string_size),
      line_size: ost$string_size,
      login_parameters: ^SEQ ( * ),
      login_string_size: ^clt$command_line_size,
      parameter_list: ^clt$parameter_list,
      password: ost$name,
      peer_accounting_information: ^string ( * ),
      peer_accounting_seq: SEQ (REP max_data_area_size of cell),
      project_name: avt$project_name,
      status: ost$status,
      terminal_name: ost$name,
      user_name: ost$user_name;


    PROCEDURE [INLINE] add_to_line
      (    text: string ( * ));

      line (line_size + 1, STRLENGTH (text)) := text;
      line_size := line_size + STRLENGTH (text);
      WHILE (line_size > 0) AND (line (line_size) = ' ') DO
        line_size := line_size - 1;
      WHILEND;

    PROCEND add_to_line;
?? OLDTITLE ??
?? NEWTITLE := '  emit_maximum_login_statistic', EJECT ??

{
{ PURPOSE:
{   This procedure is used to emit a statistic to record a possible
{   security violation.
{

    PROCEDURE emit_maximum_login_statistic
      (    user_name: ost$user_name;
           family_name: ost$family_name;
           terminal_name: ost$name);

      VAR
        counter: array [1 .. 1] of sft$counter,
        statistic_descriptive_data: ost$string,
        string_size: integer;

      statistic_descriptive_data.value := user_name;
      statistic_descriptive_data.size := clp$trimmed_string_size (statistic_descriptive_data.value);

      statistic_descriptive_data.value (statistic_descriptive_data.size + 1, 2) := ', ';
      statistic_descriptive_data.size := statistic_descriptive_data.size + 2;

      string_size := clp$trimmed_string_size (family_name);
      statistic_descriptive_data.value (statistic_descriptive_data.size + 1, string_size) :=
            family_name (1, string_size);
      statistic_descriptive_data.size := statistic_descriptive_data.size + string_size;

      statistic_descriptive_data.value (statistic_descriptive_data.size + 1, 2) := ', ';
      statistic_descriptive_data.size := statistic_descriptive_data.size + 2;

      string_size := clp$trimmed_string_size (terminal_name);
      statistic_descriptive_data.value (statistic_descriptive_data.size + 1, string_size) :=
            terminal_name (1, string_size);
      statistic_descriptive_data.size := statistic_descriptive_data.size + string_size;

      counter [1] := nav$maximum_login_attempts;

      sfp$emit_statistic (nac$maximum_login_attempts, statistic_descriptive_data.
            value (1, statistic_descriptive_data.size), ^counter, {ignore} status);

    PROCEND emit_maximum_login_statistic;
?? OLDTITLE, EJECT ??

{ Get user name and family name if the login parameters were specified in one string.

    IF login_specification = am_via_string THEN
      login_parameters := login_params;
      RESET login_parameters;
      NEXT login_string_size IN login_parameters;
      RESET login_parameters;
      NEXT parameter_list: [[REP (#SIZE (login_string_size^) + login_string_size^) OF cell]] IN
            login_parameters;
      clp$push_parameters ({ignore} status);

{ The data returned by this request will be the value supplied by the user or a null name (osc$null_name).

      clp$get_login_data_for_nam (parameter_list^, user_name, password, family_name, account_name,
            project_name, status);
      IF NOT status.normal THEN
        user_name := received_connection^.login_parameters.user_name;
        family_name := received_connection^.login_parameters.family_name;
      IFEND;
      clp$pop_parameters ({ignore} status);
    ELSE
      user_name := received_connection^.login_parameters.user_name;
      family_name := received_connection^.login_parameters.family_name;
    IFEND;

    line_size := 0;
    add_to_line ('Login retry limit exceeded by');
    add_to_line (' USER=');
    IF user_name = osc$null_name THEN
      user_name := unknown;
    IFEND;
    add_to_line (user_name);
    add_to_line (', FAMILY_NAME=');
    IF family_name = osc$null_name THEN
      default_job_attributes [1].key := jmc$login_family;
      jmp$get_attribute_defaults (jmc$batch, ^default_job_attributes, {ignore} status);
      family_name := default_job_attributes [1].login_family;
    IFEND;
    add_to_line (family_name);
    add_to_line (', TERMINAL_NAME=');

{ Retrieve the peer_accounting_information attribute.

    connection_attribute [1].kind := nac$peer_accounting_information;
    connection_attribute [1].peer_accounting_information := ^peer_accounting_seq;

    nap$get_attributes (received_connection^.network_file_name, connection_attribute, status);
    IF status.normal THEN
      IF connection_attribute [1].peer_accounting_info_length = 0 THEN
        peer_accounting_information := NIL;
      ELSE
        RESET connection_attribute [1].peer_accounting_information;
        NEXT peer_accounting_information: [connection_attribute [1].peer_accounting_info_length] IN
              connection_attribute [1].peer_accounting_information;
      IFEND;

{ Get accounting data.

      accounting_data [1].kind := nac$ca_device_name;
      nap$parse_accounting_data (peer_accounting_information, NIL, ^accounting_data, status);
      IF status.normal THEN
        terminal_name := accounting_data [1].device_name;
      ELSE
        terminal_name := unknown;
      IFEND;
    ELSE
      terminal_name := unknown;
    IFEND;
    add_to_line (terminal_name);
    pmp$log_ascii (line (1, line_size), $pmt$ascii_logset [pmc$system_log], pmc$msg_origin_system,
          {ignore} status);
    emit_maximum_login_statistic (user_name, family_name, terminal_name);

  PROCEND report_unsuccessful_login;

MODEND nam$application_event_processor;
