 


















          Message Exchange Programmer's Guide



          December 1995



          This manual describes how to customize Message
          Exchange through programming.




          Revision/Update Information:  This is a revised manual.
                                        Revision bars indicate
                                        changes made since the
                                        last version.

          Operating System and Version: VMS V5.0 or later

                                        OpenVMS AXP V1.0 or later

          Software Version:             Message Exchange V4.2

          Matt Madison and Hunter Goatley
          MadGoat Software

 








          ________________________
          11 December 1995

          Permission is granted to copy and redistribute this
          document for no commercial gain.

          The information in this document is subject to change
          without notice and should not be construed as a
          commitment by MadGoat Software. The authors and
          MadGoat Software assume no responsibility for any
          errors that may appear in this document.

          DISCLAIMER: The software described in this document is
          provided "as is". No guarantee is made by the authors
          or the authors' employers as to the suitability,
          reliability, security, usefulness, or performance
          of this software.

          MX was originally written by Matthew D. Madison,
          formerly of Rensselaer Polytechnic Institute and
          currently employed by TGV, Inc. The software is
          currently maintained by Hunter Goatley, formerly of
          Western Kentucky University and currently employed by
          The LOKI Group, Inc.

          The following are trademarks of Digital Equipment
          Corporation:

          DEC                DECnet              P.S.I.
          ULTRIX             VAX                 VAXcluster
          VMS                AXP                 VMScluster

          Jnet is a registered trademark of Wingra Technologies,
          Inc.

          MultiNet is a registered trademark of TGV, Inc.

          TCPware is a trademark of Process Software
          Corporation.

          WIN/TCP and Pathway are registered trademarks of The
          Wollongong Group, Inc.

          __________
          Copyright 1995 MadGoat Software. ALL RIGHTS RESERVED.

 









          _______________________________________________________

          Contents

                _________________________________________________
                PREFACE                                         v

          _______________________________________________________
          CHAPTER 1  THE SITE TRANSPORT INTERFACE             1-1

                _________________________________________________
                1.1   THE SITE DELIVERY INTERFACE             1-1

                _________________________________________________
                1.2   SITE MESSAGE ENTRY                      1-2


          _______________________________________________________
          CHAPTER 2  ADDRESS MODIFICATION INTERFACE           2-1

                _________________________________________________
                2.1   ADDRESS REWRITING                       2-2
                INIT                                   2-3
                REWRITE_HEADER                         2-5
                REWRITE_ENVELOPE                       2-8
                CLEANUP                               2-10

                _________________________________________________
                2.2   HOST NAME EXPANSION                    2-12
                INIT                                  2-13
                EXPAND                                2-15
                CLEANUP                               2-17

                _________________________________________________
                2.3   NAME CONVERSION                        2-19
                INIT                                  2-20
                CONVERT                               2-22
                CLEANUP                               2-25
                FULL_CONVERT                          2-27



                                                              iii

 


          Contents





          _______________________________________________________
          APPENDIX A  ADDRESS REWRITER EXAMPLE                A-1


          _______________________________________________________
          APPENDIX B  DOMAIN EXPANSION EXAMPLE                B-1


          _______________________________________________________
          APPENDIX C  NAME CONVERSION EXAMPLE                 C-1


          _______________________________________________________
          EXAMPLES

                1-1       Sample SITE_DELIVER.COM  _______    1-3
























          iv

 







          _______________________________________________________

          Preface

          Message Exchange (MX) provides two forms of
          customization: an interface for a site-specific
          transport, and interfaces for modifying addresses.
          This manual describes how to write routines or
          programs to use these interfaces for customizing MX.


          __________________________________________________________________

          Intended Audience

          This manual is intended for systems programmers who
          will be writing code to customize MX.

          __________________________________________________________________

          Document Structure

          This guide consists of two chapters and two
          appendices. Chapter 1 describes the SITE transport
          interface. Chapter 2 describes the address
          modification interfaces. The two appendices include
          sample code for illustrating the address modification
          interfaces.

          __________________________________________________________________

          Related Documents

          You can find additional information in the following
          documents:

          o  Message Exchange Installation Guide describes the
             installation of MX.

          o  Message Exchange Management Guide describes the
             management and operation of MX.

          o  Message Exchange Release Notes contain information
             and updates not included in this manual. The release
             notes are part of the software distribution kit.

                                                                v

 








          _______________________________________________________

   1      The SITE Transport Interface



          MX provides delivery agents and message entry agents
          for various transports, including TCP/IP, Jnet, and
          UUCP. If your site has some network transport that
          is not supported by MX, you can interface it with MX
          through the SITE transport interface.

          __________________________________________________________________

   1.1    The SITE Delivery Interface

          When you use the MCP DEFINE PATH command to route mail
          to the SITE path, the MX_SITE delivery agent takes
          messages routed to that path and feeds them into a
          subprocess that executes a command procedure you must
          provide. The command procedure must be called MX_
          EXE:SITE_DELIVER.COM and must accept four parameters.

          The first parameter is the "route" parameter, which
          is either the host name part of the address or the
          value of the /ROUTE qualifier from the DEFINE PATH
          command that routed the message to the SITE delivery
          agent. This parameter can be used to distinguish among
          several installed site-specific delivery agents, if
          needed.

          The second parameter is the name of a temporary file
          that contains the message, including all of the RFC822
          headers (corresponding to the DATA part of an SMTP
          transaction). The third parameter is the name of
          another temporary file that contains the recipient's
          address, corresponding to the RCPT TO addresses of
          an SMTP transaction. The fourth parameter is the
          RFC822 address of the originator of the message,
          corresponding to the MAIL FROM address of an SMTP
          transaction.

                                                              1-1

 


          The SITE Transport Interface





          Your delivery procedure and the programs it invokes
          must not cause the subprocess to terminate, nor
          should they rely on specifics about the filenames
          provided. The procedure MUST exit with a success
          status code to let MX know that the message was
          delivered successfully. If there was an error in the
          delivery and you wish MX to return an error message to
          the sender, you should exit with a non-success status
          code. If the severity of the status is SEVERE (also
          called FATAL), the SITE delivery agent will return an
          error message to the sender. Otherwise, the message
          will be queued for another attempt. The MCP SET SITE
          command controls how many attempts will be made before
          the delivery agent gives up; the default is 96, with
          30 minutes between each attempt.

          __________________________________________________________________

   1.2    SITE Message Entry

          The SITE message entry program should be used by your
          SITE transport agent to enter a message into the MX
          message queue. The program is called MX_SITE_IN and
          resides in MX_EXE. It should be invoked as a DCL
          foreign command:

              $ MX_ENTER = "$MX_EXE:MX_SITE_IN"
              $ MX_ENTER  msg-file-spec  dest-file-spec  [origin-address]

          MX_SITE_IN takes up to three parameters, which
          correspond exactly to the last three parameters passed
          out by the MX_SITE delivery agent. The first parameter
          should be the name of a file containing a properly-
          formatted RFC822 message. The second parameter should
          be the name of a file containing a list of RFC822
          route addresses (they must have the surrounding angle
          brackets, just as in an SMTP transaction). The third
          parameter, which is optional, should be the RFC822
          route address of the sender (also including the

          1-2

 


                                     The SITE Transport Interface




          Example 1-1  Sample SITE_DELIVER.COM
          _______________________________________________________

          $! Simple SITE_DELIVER.COM which invokes a real program to do all the work.
          $! This file must be placed in MX_EXE: for use with the MX SITE interface.
          $! It is invoked by MX_SITE with:
          $!
          $!  @MX_EXE:SITE_DELIVER  route msg-file-spec dest-file-spec origin-address
          $!
          $! The originator address is stuck in a file since it can contain
          $! characters that might confuse DCL when we invoke the delivery program.
          $!
          $! This is a simple procedure which ignores the "route" parameter.  If
          $! you have multiple SITE delivery paths available, use the "route" parameter
          $! to route the message to the appropriate delivery program.
          $!
          $ SET NOON
          $!
          $ DELIVER = "$my_mail_system:enter_message"
          $!
          $ CREATE my_temp_dir:SENDER.TMP
          $ OPEN/APPEND TMP my_temp_dir:SENDER.TMP
          $ WRITE TMP P4
          $ CLOSE TMP
          $!
          $ DELIVER 'P2 'P3 my_temp_dir:SENDER.TMP
          $ STAT = $STATUS
          $ DELETE my_temp_dir:SENDER.TMP;*
          $_EXIT_'STAT___________________________________________

          surrounding angle brackets). If the third parameter is
          omitted, the address of the user running the program
          will be used as the origin of the message.








                                                              1-3

 








          _______________________________________________________

   2      Address Modification Interface



          MX provides an interface for altering envelope
          addresses. This interface allows you to add your
          own routines for performing two different types of
          address modifications. For each type of modification,
          the routines must be part of a shareable library,
          which gets mapped into the appropriate parts of MX at
          run-time with LIB$FIND_IMAGE_SYMBOL.

          The address modification routines are located through
          the use of logical names.

          _______________________________________________________
          Logical_name____________Modification_type______________

          MX_SITE_ADDRESS_        Modifying headers and envelope
          REWRITER                addresses for outgoing and
                                  incoming mail

          MX_SITE_DOM_EXPANSION   Modifying or expanding host
                                  names

          MX_SITE_NAME_           Translating local aliases or
          CONVERSION______________performing_directory_lookups___

          In each case, the logical name must be defined
          /SYSTEM/EXEC and must translate to the name of
          an image that has been linked /SHARE/NOTRACE and
          INSTALLed on the system. If you name the shareable
          images ADDRESS_REWRITER.EXE, DOMAIN_EXPANSION.EXE,
          and NAME_CONVERSION.EXE, respectively, and place
          them in the MX_ROOT:[EXE] directory, the MX startup
          procedure will automatically create the logical names
          and INSTALL the shareable images for you.

                                                              2-1

 


          Address Modification Interface





          Examples of routines for performing address
          modifications are included in the directory MX_
          ROOT:[EXAMPLES] (if they have been installed).

          __________________________________________________________________

   2.1    Address Rewriting

          The site address rewriter routines are called by the
          Router process to allow RFC822 header address rewrites
          o| all outgoing mail, regardless of its origin, and on
          envelope addresses for incoming mail. The main purpose
          for these routines is to allow site-specific user and
          host naming conventions.

          The name conversion routines, described in
          Section 2.3, provide a means for implementing user
          aliases, but it does not affect domain names. With
          the address rewriter routines, both usernames and
          host names can be modified. To ease mail delivery,
          many sites prefer to hide the various machines used
          at that site by supplying a generic site name for the
          address. For example, the generic domain MADGOAT.COM
          might be used for all addresses, even though the
          machines in use may be named HUNTER.MADGOAT.COM and
          MATT.MADGOAT.COM.

          A sample address rewriter is provided in Appendix A.
          This example converts RFC822 ``From:'' addresses to
          a format like ``First.Lastname@Generic.Node'' and
          envelope addresses from that format to the actual user
          and node.

          The routines that must be provided by an address
          rewriter are described on the following pages.





          2-2

 


                                    Address Modification Interface
                                                             INIT




          _______________________________________________________

          INIT

          Initialization routine.

          _______________________________________________________

          FORMAT

          INIT  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          The INIT routine must return a success status value in
          order for the other address rewriting routines to be
          used.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is a longword passed by reference to your routine
          that you may use for any purpose, such as allocating a
          block of memory for keeping contextual information.




                                                              2-3

 


          Address Modification Interface
          INIT



          _______________________________________________________

          DESCRIPTION
          This routine is called by the Router before any calls
          to the REWRITE_HEADER and REWRITE_ENVELOPE routines.
          You may use this routine to set up any context or
          perform any housekeeping tasks needed to prepare for
          the subsequent calls to the REWRITE_* routines.

          Since your routines must be reentrant, you should
          not use static storage for keeping track of state
          information or other contextual information. Instead,
          you should allocate a block of dynamic memory and
          return its address in context.



























          2-4

 


                                    Address Modification Interface
                                                   REWRITE_HEADER




          _______________________________________________________

          REWRITE_HEADER

          Routine to rewrite an address in an RFC822 header..

          _______________________________________________________

          FORMAT

          REWRITE_HEADER  context, inaddr, outaddr, header_code

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          To indicate a successful rewrite, return SS$_NORMAL or
          some other success status code. If you do not return a
          success status code, the caller will assume that the
          rewrite did not occur.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is the same value that was passed to the INIT
          routine.

          inaddr

          VMS Usage: char_string
          type:      character string
          access:    read only
          mechanism:  by descriptor (fixed-length)
          The RFC822 header address to be rewritten.

                                                              2-5

 


          Address Modification Interface
          REWRITE_HEADER




          outaddr

          VMS Usage: char_string
          type:      character string
          access:    write only
          mechanism: by descriptor
          A string into which your routine should copy the
          rewritten address, if expansion was successful. You
          must use the STR$ string routines (such as STR$COPY_
          DX) to copy the string into this argument.

          header_code

          type:      longword (unsigned)
          access:    read only
          mechanism: by value
          A value representing one of the following RFC822
          header types:

          _______________________________________________________
          Description____Symbolic_name___________Value___________

          From:          MX_K_HDR_FROM           1

          Sender:        MX_K_HDR_SENDER         2

          To:            MX_K_HDR_TO             3

          Resent-To:     MX_K_HDR_R_TO           4

          CC:            MX_K_HDR_CC             5

          Resent-CC:     MX_K_HDR_R_CC           6

          BCC:           MX_K_HDR_BCC            7

          Resent-BBC:    MX_K_HDR_R_BCC          8

          Reply-To:      MX_K_HDR_REPLY_TO       17

          Resent-        MX_K_HDR_R_REPLY_TO     19
          Reply-To:

          Resent-From:   MX_K_HDR_R_FROM         20

          Resent-        MX_K_HDR_R_SENDER       21
          Sender:________________________________________________

          The symbolic names are defined in MX_HDR.H in MX_
          ROOT:[EXAMPLES], if you installed the examples.

          2-6

 


                                    Address Modification Interface
                                                   REWRITE_HEADER



          _______________________________________________________

          DESCRIPTION
          This routine is called to rewrite an address appearing
          in an RFC822 header on outgoing mail originating
          from VMS Mail. The address of the context block you
          allocated in the INIT routine is passed in here for
          any information you need to keep track of between
          calls. This routine may be called more than once
          between one pair of INIT and CLEANUP calls.































                                                              2-7

 


          Address Modification Interface
          REWRITE_ENVELOPE




          _______________________________________________________

          REWRITE_ENVELOPE

          Routine to rewrite an RFC821 envelope address.

          _______________________________________________________

          FORMAT

          REWRITE_ENVELOPE  context, inaddr, outaddr

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          To indicate a successful rewrite, return SS$_NORMAL or
          some other success status code. If you do not return a
          success status code, the caller will assume that the
          rewrite did not occur.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is the same value that was passed to the INIT
          routine.

          inaddr

          VMS Usage: char_string
          type:      character string
          access:    read only
          mechanism:  by descriptor (fixed-length)

          2-8

 


                                    Address Modification Interface
                                                 REWRITE_ENVELOPE




          The RFC821 envelope address to be rewritten. RFC821
          addresses are enclosed in angle brackets (<>). For
          example, ``<GENE@MADGOAT.COM>'' is a valid RFC821
          envelope address.

          outaddr

          VMS Usage: char_string
          type:      character string
          access:    write only
          mechanism: by descriptor
          A string into which your routine should copy the
          rewritten address, if expansion was successful. You
          must use the STR$ string routines (such as STR$COPY_
          DX) to copy the string into this argument.

          Note: The rewritten address must be a valid RFC821
          address, including the angle brackets.

          _______________________________________________________

          DESCRIPTION
          This routine is called to rewrite an RFC821 envelope
          address on incoming mail. Envelope addresses are the
          addresses of the actual recipients of incoming mail
          and may or may not correspond directly to the RFC822
          headers.

          The address of the context block you allocated in the
          INIT routine is passed in here for any information you
          need to keep track of between calls. This routine may
          be called more than once between one pair of INIT and
          CLEANUP calls.







                                                              2-9

 


          Address Modification Interface
          CLEANUP




          _______________________________________________________

          CLEANUP

          Context cleanup routine.

          _______________________________________________________

          FORMAT

          CLEANUP  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          This routine should return a status value indicating
          the success or failure of the cleanup operation. The
          caller may or may not ignore the returned value.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          The address of the context block you allocated in the
          INIT routine.

          _______________________________________________________

          DESCRIPTION
          This routine is called to clean up after a series
          of REWRITE_* calls. You should clean up the context
          information and deallocate the context block allocated
          by the INIT routine.

          2-10

 


                                    Address Modification Interface
                                                          CLEANUP




          If you did not allocate a context block in the INIT
          routine, you must still have a CLEANUP routine, even
          if it just returns to the caller.





































                                                             2-11

 


          Address Modification Interface




          __________________________________________________________________

   2.2    Host Name Expansion

          The site host name routines are called by the Router
          process just before path identification. The main
          purpose for these routines is to expand abbreviated
          host names into full host names that will properly
          match one of the paths defined in the MX configuration
          file.

          When you install SMTP support with MX, host name
          expansion routines are automatically provided that
          call on the underlying TCP/IP package to catch
          abbreviated host names that might be recognized by
          the TCP/IP name resolver but are not defined in the
          MX configuration file. The source for these routines
          is included in MX_ROOT:[EXAMPLES] for you to modify if
          needed.

          Another possible use for host name expansion is for
          sites running Jnet. Normally, the Router identifies
          a BITNET-destined message by looking for the .BITNET
          suffix on the host name. A local host name expander
          could be used to allow users to just use the BITNET
          node name without a suffix. Each host name could be
          checked by the expander against a BITNET host name
          table; a matching name would get the .BITNET suffix
          appended.

          A sample host name expander is provided in Appendix B.

          The routines that must be provided by a host name
          expander are described on the following pages.







          2-12

 


                                    Address Modification Interface
                                                             INIT




          _______________________________________________________

          INIT

          Initialization routine.

          _______________________________________________________

          FORMAT

          INIT  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          The INIT routine must return a success status value in
          order for the other expansion routines to be used.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is a longword passed by reference to your routine
          that you may use for any purpose, such as allocating a
          block of memory for keeping contextual information.

          _______________________________________________________

          DESCRIPTION
          This routine is called by the Router before any calls
          to the EXPAND routine. You may use this routine to
          set up any context or perform any housekeeping tasks
          needed to prepare for the subsequent calls to EXPAND.

                                                             2-13

 


          Address Modification Interface
          INIT




          Since your routines must be reentrant, you should
          not use static storage for keeping track of state
          information or other contextual information. Instead,
          you should allocate a block of dynamic memory and
          return its address in context.



































          2-14

 


                                    Address Modification Interface
                                                           EXPAND




          _______________________________________________________

          EXPAND

          Routine to expand a host name.

          _______________________________________________________

          FORMAT

          EXPAND  context, hostname, expname

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          To indicate a successful expansion, return SS$_NORMAL
          or some other success status code. If you do not
          return a success status code, the caller will assume
          that expansion did not occur.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is the same value that was passed to the INIT
          routine.

          hostname

          VMS Usage: char_string
          type:      character string
          access:    read only
          mechanism:  by descriptor (fixed-length)
          The host name to be expanded.

                                                             2-15

 


          Address Modification Interface
          EXPAND




          expname

          VMS Usage: char_string
          type:      character string
          access:    write only
          mechanism: by descriptor
          A string into which your routine should copy the
          expanded host name, if expansion was successful. You
          must use the STR$ string routines (such as STR$COPY_
          DX) to copy the string into this argument.

          _______________________________________________________

          DESCRIPTION
          This routine is called to perform a host name
          expansion. The address of the context block you
          allocated in the INIT routine is passed in here for
          any information you need to keep track of between
          calls. This routine may be called more than once
          between one pair of INIT and CLEANUP calls.




















          2-16

 


                                    Address Modification Interface
                                                          CLEANUP




          _______________________________________________________

          CLEANUP

          Context cleanup routine.

          _______________________________________________________

          FORMAT

          CLEANUP  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          This routine should return a status value indicating
          the success or failure of the cleanup operation. The
          caller may or may not ignore the returned value.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          The address of the context block you allocated in the
          INIT routine.

          _______________________________________________________

          DESCRIPTION
          This routine is called to clean up after a series
          of EXPAND calls. You should clean up the context
          information and deallocate the context block allocated
          by the INIT routine.

                                                             2-17

 


          Address Modification Interface
          CLEANUP




          If you did not allocate a context block in the INIT
          routine, you must still have a CLEANUP routine, even
          if it just returns to the caller.





































          2-18

 


                                   Address Modification Interface




          __________________________________________________________________

   2.3    Name Conversion

          The local name conversion routines are used by the
          MX_MAILSHR VMS Mail interface to translate a username
          into an alias and by the Router to translate aliases
          back into real usernames. This can be used, for
          example, to map usernames into "real" names and vice-
          versa.

          A sample name conversion module is provided in
          Appendix C.

          The following pages describe the routines that must be
          provided for the name conversion interface.

          In addition to the required CONVERT routine, an
          optional FULL_CONVERT routine may be provided to allow
          for conversion of a username to a full RFC822 address,
          as opposed to just username substitution.




















                                                             2-19

 


          Address Modification Interface
          INIT




          _______________________________________________________

          INIT

          Initialization routine.

          _______________________________________________________

          FORMAT

          INIT  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          The INIT routine must return a success status value in
          order for the other expansion routines to be used.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is a longword passed by reference to your routine
          that you may use for any purpose, such as allocating a
          block of memory for keeping contextual information.

          _______________________________________________________

          DESCRIPTION
          This routine is called by the Router before any calls
          to the CONVERT routine. You may use this routine to
          set up any context or perform any housekeeping tasks
          needed to prepare for the subsequent calls to CONVERT.

          2-20

 


                                    Address Modification Interface
                                                             INIT




          Since your routines must be reentrant, you should
          not use static storage for keeping track of state
          information or other contextual information. Instead,
          you should allocate a block of dynamic memory and
          return its address in context.



































                                                             2-21

 


          Address Modification Interface
          CONVERT




          _______________________________________________________

          CONVERT

          Routine to convert a username to an alias or an alias
          to a username.

          _______________________________________________________

          FORMAT

          CONVERT  context, code, inname, outname

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          On successful conversion, return SS$_NORMAL or some
          other success status code. If you do not return a
          success status code, the caller will assume that
          expansion did not occur.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is the same value that was passed to the INIT
          routine.

          code

          VMS Usage: longword_unsigned
          type:      longword (unsigned)
          access:    read only
          mechanism: by reference

          2-22

 


                                    Address Modification Interface
                                                          CONVERT




          This argument indicates what type of name conversion
          should occur. It will have one of the following
          values:

          _______________________________________________________
          Value______Meaning_____________________________________

          1          Perform alias-to-username conversion.

          2__________Perform_username-to-alias_conversion._______

          inname

          VMS Usage: char_string
          type:      character string
          access:    read only
          mechanism:  by descriptor (fixed-length)
          The name to be converted.

          outname

          VMS Usage: char_string
          type:      character string
          access:    write only
          mechanism: by descriptor
          A string into which your routine should copy the
          result. This is only used if you return a success
          status code.

          _______________________________________________________

          DESCRIPTION
          This routine is called to perform a name conversion.
          For alias-to-username translation, a string containing
          the potential alias is passed in inname. If the
          conversion succeeds, the address returned in outname
          must be in RFC821 format:

                                         <username@hostname>

          This format must be used even if the address is
          intended for the local host.

                                                             2-23

 


          Address Modification Interface
          CONVERT




          For username-to-alias conversion, the username to
          be converted is passed in inname. If no conversion
          is performed, return a non-success status code;
          otherwise, provide a result in outname. The result
          should be only the local part of an address; no host
          name should be appended nor any punctuation added.

          This routine may be called more than once between one
          pair of INIT and CLEANUP calls.































          2-24

 


                                    Address Modification Interface
                                                          CLEANUP




          _______________________________________________________

          CLEANUP

          Context cleanup routine.

          _______________________________________________________

          FORMAT

          CLEANUP  context

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          This routine should return a status value indicating
          the success or failure of the cleanup operation. The
          caller may or may not ignore the returned value.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          The address of the context block you allocated in the
          INIT routine.

          _______________________________________________________

          DESCRIPTION
          This routine is called to clean up after a series
          of CONVERT calls. You should clean up the context
          information and deallocate the context block allocated
          by the INIT routine.

                                                             2-25

 


          Address Modification Interface
          CLEANUP




          If you did not allocate a context block in the INIT
          routine, you must still have a CLEANUP routine, even
          if it just returns to the caller.





































          2-26

 


                                    Address Modification Interface
                                                     FULL_CONVERT




          _______________________________________________________

          FULL_CONVERT

          Routine to convert a username to an alias (as a full
          RFC822 address).

          _______________________________________________________

          FORMAT

          FULL_CONVERT  context, code, inname, outname

          _______________________________________________________

          RETURNS
          VMS Usage: cond_value
          type:      longword (unsigned)
          access:    write only
          mechanism: by value

          On successful conversion, return SS$_NORMAL or some
          other success status code. If you do not return a
          success status code, the caller will assume that
          conversion did not occur.

          _______________________________________________________

          ARGUMENTS
          context

          VMS Usage: context
          type:      longword (unsigned)
          access:    modify
          mechanism: by reference
          This is the same value that was passed to the INIT
          routine.

          code

          VMS Usage: longword_unsigned
          type:      longword (unsigned)
          access:    read only
          mechanism: by reference

                                                             2-27

 


          Address Modification Interface
          FULL_CONVERT




          This argument indicates what type of name conversion
          should occur. Only the following value should be
          accepted:

          _______________________________________________________
          Value______Meaning_____________________________________

          2__________Perform_username-to-alias_conversion._______

          All other values for this argument are reserved for
          future use.

          inname

          VMS Usage: char_string
          type:      character string
          access:    read only
          mechanism:  by descriptor (fixed-length)
          The name to be converted.

          outname

          VMS Usage: char_string
          type:      character string
          access:    write only
          mechanism: by descriptor
          A string into which your routine should copy the
          result. This is only used if you return a success
          status code.

          _______________________________________________________

          DESCRIPTION
          This routine is called to perform a username-to-full-
          address conversion. The username to be converted
          is passed in inname. If no conversion is performed,
          return a non-success status code. Unlike the CONVERT
          routine, the result you provide in outname on a
          successful conversion must be a full RFC822-type
          address (user@host format).

          This routine may be called more than once between one
          pair of INIT and CLEANUP calls, and may be intermixed
          with CONVERT calls.

          2-28

 








          _______________________________________________________

   A      Address Rewriter Example



          This is an example of an address rewriter module,
          written in C by Andrew Greer and Hunter Goatley.

          #define module_name ADDRESS_REWRITER
          #define module_ident "V1.0"
          /*
          !++
          !
          ! MODULE:       ADDRESS_REWRITER.C
          !
          ! ABSTRACT:     Example of site-installable rewrite rules
          !
          ! AUTHOR: Andrew Greer <Andrew.Greer@vuw.ac.nz>
          !  Hunter Goatley <goathunter@WKUVX1.WKU.EDU>
          !  Copyright  1994, MadGoat Software.  All rights reserved.
          !
          ! MODULE DESCRIPTION:
          !
          !   This module contains routines for use by MX modules (specifically the
          !   MX_ROUTER agent process) for rewriting RFC822 addresses.
          !
          !   To build it, use:
          !
          ! $ CC ADDRESS_REWRITER
          ! $ LINK/NOTRACE/SHARE ADDRESS_REWRITER.OBJ, SYS$INPUT:/OPTION
          ! SYS$SHARE:VAXCRTL.EXE/SHARE
          ! UNIVERSAL=INIT,REWRITE_HEADER,REWRITE_ENVELOPE,CLEANUP
          ! ^Z
          ! $
          !
          !   On AXP, use:
          !
          ! $ CC ADDRESS_REWRITER

                                                              A-1

 


          Address Rewriter Example





          ! $ LINK/NOTRACE/SHARE ADDRESS_REWRITER.OBJ, SYS$INPUT:/OPTION
          ! SYMBOL_VECTOR=(-
          !    INIT   = PROCEDURE,-
          !    REWRITE_HEADER = PROCEDURE,-
          !    REWRITE_ENVELOPE = PROCEDURE,-
          !    CLEANUP  = PROCEDURE)
          ! ^Z
          ! $
          !
          !   Then copy it to MX_EXE: and make it available to the Router with the
          !   following commands:
          !
          !       $ COPY ADDRESS_REWRITER.EXE MX_EXE:
          !       $ DEFINE/SYSTEM/EXEC MX_SITE_ADDRESS_REWRITER MX_EXE:ADDRESS_REWRITER
          !       $ MCP RESET ROUTER
          !
          !   Format of the file used is:
          !
          ! USERNAME ALIAS
          !
          !   where the username has a maximum length of 12 characters and the alias
          !   has a maximum length of 33 chars.
          !
          !   For example:
          !
          ! goathunter Hunter.Goatley
          !
          ! A lot of this is stolen directory from the NAME_CONVERSION routines provided
          ! as an example of CONVERT and FULL_CONVERT
          !
          ! Basically rewrite the FROM/RESENT_FROM headers to match the Email address
          ! format we use. Also rewrite the Envelope so that the mail gets delivered to
          ! the username that matches that Email address.
          !
          ! E.g. mail from andrew@matai.vuw.ac.nz will get the From: rewritten as
          !   Andrew.Greer@vuw.ac.nz
          ! And mail coming into Andrew.Greer@vuw.ac.nz will get delivered to
          ! andrew@matai.vuw.ac.nz
          !
          !  The following logicals must be defined:

          A-2

 


                                         Address Rewriter Example





          !
          ! MX_NODE_NAME  - The node name (e.g., WKUVX1.WKU.EDU)
          ! MX_SITE_GENERIC  - The generic node name for outgoing mail
          !      (for example, WKU.EDU)
          ! MX_SITE_ALIASES_TMP - The name of the alias file
          !
          !--
          */

          #if defined(__DECC) || defined(__DECCXX)
          #pragma module module_name module_ident
          #else
          #module module_name module_ident
          #endif

          #include <ctype.h>
          #include <descrip.h>
          #include <lnmdef.h>
          #include <stdio.h>
          #include <string.h>
          #include <ssdef.h>
          #include <lib$routines.h>
          #include <str$routines.h>

          #include "mx_hdr.h"

          #define MIN(a,b)    (a < b ? a : b)

          static $DESCRIPTOR(lbrack, "<");
          static $DESCRIPTOR(rbrack, ">");
          static $DESCRIPTOR(atsign, "@");

          struct context {
           struct dsc$descriptor localnode, generic;
           int num_names;
           struct {
               char user[13];
               char alias[34];
               char ret[34];
                  } names[100];
          };

          /* init_dynamic_descriptor - initializes dynamic descriptor */

                                                              A-3

 


          Address Rewriter Example





          static void init_dynamic_descriptor(struct dsc$descriptor *d)
          {
                  d->dsc$b_dtype = DSC$K_DTYPE_T;
                  d->dsc$b_class = DSC$K_CLASS_D;
                  d->dsc$w_length = 0;
                  d->dsc$a_pointer = NULL;
          }

          /* init_static_descriptor - initializes static descriptor */

          static void init_static_descriptor(struct dsc$descriptor *d, char *string)
          {
                  d->dsc$b_dtype = DSC$K_DTYPE_T;
                  d->dsc$b_class = DSC$K_CLASS_S;
                  d->dsc$w_length = strlen(string);
                  d->dsc$a_pointer = string;
          }

          int check_name(struct context **ctx, struct dsc$descriptor *inname,
                         struct dsc$descriptor *outname)
          {
            int idx;
            char user[64];
            struct dsc$descriptor tmpdsc;

            strncpy (user, inname->dsc$a_pointer, inname->dsc$w_length);
            user [inname->dsc$w_length] = '\0';

            for(idx=0; idx < strlen(user); idx++) {
              if (isupper(user[idx]))
                user[idx] = tolower(user[idx]);
            }

            for(idx=0; idx < ((*ctx)->num_names); idx++)
             {
              if (!strcmp ((*ctx)->names[idx].alias, user))
               {
                init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].user);
                str$copy_dx(outname, &tmpdsc);
                return SS$_NORMAL;
               }
             } /* for */

          A-4

 


                                         Address Rewriter Example





            return 0;

          } /* check_name */

          int get_alias(struct context **ctx, struct dsc$descriptor *inname,
                         struct dsc$descriptor *outname)
          {
            int idx;
            char user[64];
            struct dsc$descriptor tmpdsc;

            strncpy(user, inname->dsc$a_pointer, inname->dsc$w_length);
            user[inname->dsc$w_length] = '\0';

            for(idx=0; idx < strlen(user); idx++)
              if (isupper(user[idx]))
                user[idx] = tolower(user[idx]);

            for(idx=0; idx < (*ctx)->num_names; idx++)
             {
              if (!strcmp((*ctx)->names[idx].user, user))
               {
                init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].ret);
                str$copy_dx(outname, &tmpdsc);
                return SS$_NORMAL;
               }
             } /* for */

            return 0;

          } /* get_alias */









                                                              A-5

 


          Address Rewriter Example





          /*
          !++
          !
          ! ROUTINE NAME:     INIT
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Allocates and initializes context block for subsequent name conversions.
          !
          ! RETURNS:      cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   INIT  ctxptr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:         normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */
          unsigned int
          init (struct context **ctx) {

              int ctxsize, status, idx, idx2;
              $DESCRIPTOR(mx_node_name, "MX_NODE_NAME");
              $DESCRIPTOR(mx_site_generic, "MX_SITE_GENERIC");
              $DESCRIPTOR(mx_site_aliases, "MX_SITE_ALIASES_TMP");
              char alias_file[64];
              struct dsc$descriptor aliasdsc;
              FILE *fd;

          A-6

 


                                         Address Rewriter Example





              ctxsize = sizeof(struct context);
              status = lib$get_vm (&ctxsize, ctx);
              if (status != SS$_NORMAL) return status;

              init_dynamic_descriptor(&(*ctx)->localnode);
              init_dynamic_descriptor(&(*ctx)->generic);
              init_dynamic_descriptor(&aliasdsc);

              status = lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode);
              if (status != SS$_NORMAL) return status;

              status = lib$sys_trnlog (&mx_site_generic, 0, &(*ctx)->generic);
              if (status != SS$_NORMAL) return status;

              status = lib$sys_trnlog (&mx_site_aliases, 0, &aliasdsc);
              if (status != SS$_NORMAL) return status;

              strncpy(alias_file, aliasdsc.dsc$a_pointer, aliasdsc.dsc$w_length);
              alias_file[aliasdsc.dsc$w_length] = '\0';

              if (fd = fopen(alias_file, "r"))
               {
                (*ctx)->num_names = 100;
                for (idx=0; idx < (*ctx)->num_names; idx++)
                 {
                  fscanf(fd, "%s %s", &(*ctx)->names[idx].user,
            &(*ctx)->names[idx].ret);

           /* Convert the alias to lowercase for matching later */
           for(idx2=0; idx2 < strlen((*ctx)->names[idx].ret); idx2++) {
                 (*ctx)->names[idx].alias[idx2] =
             tolower((*ctx)->names[idx].ret[idx2]);
             }

                  if (feof(fd))
                   {
                    (*ctx)->num_names = idx;
                    break;
                   }
                 } /* for */
               }
              else
                return 0;

                                                              A-7

 


          Address Rewriter Example





              return SS$_NORMAL;
          }  /* init */

          /*
          !++
          !
          ! ROUTINE NAME:     REWRITE_HEADER
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          ! RETURNS:      cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   REWRITE_HEADER ctxptr, instr, outstr, code
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          ! instr:    char_string, character string, read only, by descriptor (fixed)
          ! outstr:   char_string, character string, write only, by descriptor
          ! code:     word_unsigned, word (unsigned), read only, by value/reference?
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:         normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */
          unsigned int
          rewrite_header( struct context **ctx,
                          struct dsc$descriptor *inadr,
                          struct dsc$descriptor *outadr,
                          unsigned short code )
          {

          A-8

 


                                         Address Rewriter Example





            int   rc,
                  len,
                  pos,
                  start_pos,
                  end_pos;
            struct dsc$descriptor localdsc, domdsc, newlocaldsc;

            init_dynamic_descriptor(&newlocaldsc);
            init_dynamic_descriptor(&localdsc);
            init_dynamic_descriptor(&domdsc);

            pos = str$position(inadr, &atsign);
            if (pos > 0)
             {
              start_pos = 1;
              end_pos = pos - 1;
              str$len_extr(&localdsc, inadr, &start_pos, &end_pos);
              start_pos = pos + 1;
              end_pos = inadr->dsc$w_length - pos;
              str$len_extr(&domdsc, inadr, &start_pos, &end_pos);

             }

            switch (code)
             {
              /* Possible headers that could be rewritten (from [MX.ROUTER]PROCESS.B32) */
              case MX_K_HDR_FROM:             /* From */
              case MX_K_HDR_R_FROM:           /* Resent From */
                      rc = str$case_blind_compare(&domdsc, &(*ctx)->localnode);
                      if (get_alias(ctx, &localdsc, &newlocaldsc))
                       {
                        str$concat(outadr, &newlocaldsc, &atsign, &(*ctx)->generic);
                        return SS$_NORMAL;
                       }
                      break;
              case MX_K_HDR_TO:               /* To */
              case MX_K_HDR_R_TO:             /* Resent To */
              case MX_K_HDR_CC:               /* CC */
              case MX_K_HDR_R_CC:             /* Resent CC */
              case MX_K_HDR_BCC:              /* BCC */
              case MX_K_HDR_R_BCC:            /* Resent BCC */
              case MX_K_HDR_REPLY_TO:         /* Reply To */

                                                              A-9

 


          Address Rewriter Example





              case MX_K_HDR_SENDER:           /* Sender */
              case MX_K_HDR_R_SENDER:         /* Resent Sender */
              case MX_K_HDR_R_REPLY_TO:       /* Resent Reply To */
              default:
                      break;
             }

            return 0;
          } /* rewrite_header */

          /*
          !++
          !
          ! ROUTINE NAME:     REWRITE_ENVELOPE
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          ! RETURNS:      cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   REWRITE_ENVELOPE ctxptr, inadr, outadr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          ! instr:    char_string, character string, read only, by descriptor (fixed)
          ! outstr:   char_string, character string, write only, by descriptor
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:         normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */

          A-10

 


                                         Address Rewriter Example





          unsigned int
          rewrite_envelope( struct context **ctx,
                            struct dsc$descriptor *inadr,
                            struct dsc$descriptor *outadr )
          {
            int   rc,
                  len,
                  pos,
                  start_pos,
                  end_pos;
            struct dsc$descriptor localdsc, domdsc, newlocaldsc;

            init_dynamic_descriptor(&newlocaldsc);
            init_dynamic_descriptor(&localdsc);
            init_dynamic_descriptor(&domdsc);

            pos = str$position(inadr, &atsign);
            if (pos > 0)
             { /* Remove the "<" and ">" */
              start_pos = str$position(inadr, &lbrack) + 1;
              end_pos = pos - 2;
              str$len_extr(&localdsc, inadr, &start_pos, &end_pos);
              start_pos = pos + 1;
              end_pos = str$position(inadr, &rbrack);
              if (end_pos > 0)
                end_pos = end_pos - start_pos;
              else
                end_pos = inadr->dsc$w_length - pos;
              str$len_extr(&domdsc, inadr, &start_pos, &end_pos);
             }

            rc = str$case_blind_compare(&domdsc, &(*ctx)->generic);
            if (rc != 0)
              return 0;

            if (check_name(ctx, &localdsc, &newlocaldsc))
             {
              str$concat(outadr, &lbrack, &newlocaldsc, &atsign, &(*ctx)->localnode,
                      &rbrack);
              return SS$_NORMAL;
             }

                                                             A-11

 


          Address Rewriter Example





            return 0;

          } /* rewrite_envelope */

          /*
          */
          unsigned int
          cleanup (struct context **ctx) {

              int ctxsize, status;

              status = str$free1_dx(&(*ctx)->localnode);
              if (status != SS$_NORMAL) return status;

              status = str$free1_dx(&(*ctx)->generic);
              if (status != SS$_NORMAL) return status;

              ctxsize = sizeof(struct context);
              status = lib$free_vm (&ctxsize, ctx);
              if (status != SS$_NORMAL) return status;

              *ctx = NULL;
              return SS$_NORMAL;
          }  /* cleanup */

          #ifdef MAIN
          int main(void)
          {
             $DESCRIPTOR(x, "goathunter@NUKE2.WKU.EDU");
             $DESCRIPTOR(y, "<Hunter.goatley@NUKE2.WKU.EDU>");
             struct dsc$descriptor outstr, outstr2;
             struct context *ctx;
             int status;

             init_dynamic_descriptor (&outstr);
             init_dynamic_descriptor (&outstr2);

             init (&ctx);
             status = rewrite_header (&ctx, &x, &outstr, MX_K_HDR_FROM);
             lib$put_output (&outstr);
             status = rewrite_envelope (&ctx, &y, &outstr);
             lib$put_output (&outstr);

          A-12

 


                                         Address Rewriter Example





             return(SS$_NORMAL);
          }
          #endif





































                                                             A-13

 








          _______________________________________________________

   B      Domain Expansion Example



          This is an example of a domain name expansion module
          for use with CMU-Tek TCP/IP, written in BLISS.

          %TITLE 'DOM_EXPANSION_CMU'
          MODULE DOM_EXPANSION_CMU (IDENT='V1.0', ADDRESSING_MODE (EXTERNAL=GENERAL)) =
          BEGIN
          !++
          ! FACILITY:      MX Examples
          !
          ! ABSTRACT:      Example of a domain name expander for use with MX.
          !             For use with CMU-Tek TCP/IP.
          !
          ! MODULE DESCRIPTION:
          !
          !   This module contains the routines necessary for implementing
          !   a domain name expander for use by the MX Router agent.  These
          !   routines can be used to eliminate SMTP mail loops when mail
          !   is addressed using an abbreviated host name, without having
          !   to DEFINE PATH LOCAL for each abbreviation.
          !
          !   To use this module: modify it as needed, then compile it
          !   and link it with the commands:
          !
          !    $ BLISS DOM_EXPANSION_CMU
          !    $ LINK/SHARE=DOM_EXPANSION/NOTRACE DOM_EXPANSION_CMU,SYS$INPUT:/OPT
          !        UNIVERSAL=INIT,EXPAND,CLEANUP
          !        <ctrl/Z>
          !
          !   Then copy it to MX_EXE and make it available to the Router with
          !   the commands:
          !
          !    $ COPY DOM_EXPANSION.EXE MX_EXE:
          !    $ DEFINE/SYSTEM/EXEC MX_SITE_DOM_EXPANSION MX_EXE:DOM_EXPANSION

                                                              B-1

 


          Domain Expansion Example





          !    $ MCP RESET ROUTER
          !
          !   (You need a suitably privileged account to do this.)
          !
          ! AUTHOR:        M. Madison
          !      Copyright  1993,1994, MadGoat Software.  All Rights Reserved.
          !
          ! CREATION DATE:    07-DEC-1990
          !
          ! MODIFICATION HISTORY:
          !
          !   07-DEC-1990 V1.0 Madison     Initial coding.
          !--
              LIBRARY 'SYS$LIBRARY:STARLET';
              LIBRARY 'SYS$LIBRARY:NETWORK';

              EXTERNAL ROUTINE
               STR$CONCAT, STR$COPY_R, STR$FREE1_DX, LIB$GET_VM,
               LIB$FREE_VM;

              LITERAL
               CTX_S_CTXDEF = 2;

              FIELD
               CTX_FIELDS =
               SET
                   CTX_W_CHAN =   [0,0,16,0]
               TES;

              MACRO
               CTXDEF = BLOCK [CTX_S_CTXDEF,BYTE] FIELD (CTX_FIELDS)%;









          B-2

 


                                         Domain Expansion Example





          %SBTTL 'INIT'
          GLOBAL ROUTINE INIT (CTX_A_A) =
          BEGIN
          !++
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Called by the Router to initialize the module.  Could be used to
          !   allocate any storage that will be needed by the EXPAND routine
          !   (these routines must be reentrant, so OWN storage is right out).
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   INIT  ctxptr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
              BIND
               CTX  = .CTX_A_A   : REF CTXDEF;

              LOCAL
               STATUS;





                                                              B-3

 


          Domain Expansion Example





              STATUS = LIB$GET_VM (%REF (CTX_S_CTXDEF), CTX);
              IF .STATUS THEN
              BEGIN
               STATUS = $ASSIGN (DEVNAM=%ASCID'IP0', CHAN=CTX [CTX_W_CHAN]);
               IF NOT .STATUS THEN LIB$FREE_VM (%REF (CTX_S_CTXDEF), CTX);
              END;

              .STATUS

          END; ! INIT

          %SBTTL 'EXPAND'
          GLOBAL ROUTINE EXPAND (CTX_A_A, INSTR_A, OUTSTR_A) =
          BEGIN
          !++
          ! FUNCTIONAL DESCRIPTION:
          !
          !   This routine is called to perform a domain name expansion.
          !
          !   INSTR can be assumed to be a DTYPE_T, CLASS_S string descriptor
          !   (or compatible).  You must use STR$ routines to copy the result
          !   to OUTSTR!
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   EXPAND  ctxptr, instr, outstr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          ! instr:    char_string, character string, read only, by descriptor
          ! outstr:   char_string, character string, write only, by descriptor
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !

          B-4

 


                                         Domain Expansion Example





          ! SIDE EFFECTS:
          !
          !   None.
          !--
              BIND
               CTX  = .CTX_A_A       : REF CTXDEF,
               CHN  = CTX [CTX_W_CHAN]  : WORD,
               INSTR = .INSTR_A       : BLOCK [DSC$K_S_BLN,BYTE],
               OUTSTR = .OUTSTR_A      : BLOCK [DSC$K_S_BLN,BYTE];

              LOCAL
               GHBLK : GTHST_NMLOOK_BLOCK,
               IOSB : NETWORK_IOSB,
               STR  : BLOCK [DSC$K_S_BLN,BYTE],
               STATUS;

              $INIT_DYNDESC (STR);
              STR$CONCAT (STR, INSTR, %ASCID %STRING(%CHAR (0)));
              STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK),
               GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,
               IO$SB=IOSB);
              IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THEN
                              .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]);
              IF NOT .STATUS AND
                   CH$RCHAR (.INSTR [DSC$A_POINTER]+.INSTR [DSC$W_LENGTH]-1) NEQ %C'.'
              THEN
              BEGIN
               STR$CONCAT (STR, INSTR, %ASCID %STRING ('.', %CHAR (0)));
               STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK),
                   GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,
                   IO$SB=IOSB);
               IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THEN
                              .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]);
              END;
              STR$FREE1_DX (STR);
              IF .STATUS THEN STATUS = STR$COPY_R (OUTSTR, GHBLK [GH$NL_NAMLEN],
                                  GHBLK [GH$NL_NAMSTR]);
              .STATUS

          END; ! EXPAND

                                                              B-5

 


          Domain Expansion Example





          %SBTTL 'CLEANUP'
          GLOBAL ROUTINE CLEANUP (CTX_A_A) =
          BEGIN
          !++
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Called by the Router to clean up any context info set up by
          !   INIT.
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   CLEANUP  ctxptr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
              BIND
               CTX  = .CTX_A_A   : REF CTXDEF;

              $DASSGN (CHAN=.CTX [CTX_W_CHAN]);
              LIB$FREE_VM (%REF (CTX_S_CTXDEF), CTX);
              CTX = 0;

              SS$_NORMAL

          END; ! CLEANUP

          END
          ELUDOM

          B-6

 








          _______________________________________________________

   C      Name Conversion Example



          This is an example of a simple name conversion module,
          written in C.

          /*
          !++
          !
          ! MODULE:        NAME_CONVERSION
          !
          ! FACILITY:      MX examples
          !
          ! ABSTRACT:      Example of site-installable nickname conversion.
          !
          ! MODULE DESCRIPTION:
          !
          !   This module contains routines for use by MX modules (specifically,
          !   the MX_MAILSHR interface to VMS Mail and the MX_ROUTER agent process)
          !   for translating between actual VMS usernames and site-specific aliases.
          !
          !   This module contains a fairly primitive lookup table to implement
          !   the translation.
          !
          !   To use this module: MODIFY IT AS NEEDED FOR YOUR SITE, then compile it
          !   and link it with the commands:
          !
          !       $ cc name_conversion
          !    $ link/share/notrace name_conversion,sys$input:/opt
          !        sys$share:vaxcrtl/share
          !        universal=init,convert,full_convert,cleanup
          !        <ctrl/Z>
          !
          !   Then copy it to MX_EXE and make it available with the commands:
          !
          !    $ copy name_conversion.exe mx_exe:/protection=w:re

                                                              C-1

 


          Name Conversion Example





          !    $ install create mx_exe:name_conversion/share/open/header
          !    $ define/system/exec mx_site_name_conversion mx_exe:name_conversion
          !    $ mcp reset router  ! to force Router to load the code
          !
          !   (You need a suitably privileged account to do this.)
          !
          ! AUTHOR:        M. Madison
          !      Copyright  1993,1994, MadGoat Software.  All Rights Reserved.
          !
          !   THIS SOFTWARE IS PROVIDE "AS IS".  NEITHER THE AUTHOR NOR MadGoat
          !   MAKE ANY GUARANTEES REGARDING THE SUITABILITY, RELIABILITY, SECURITY,
          !   USEFULNESS, OR PERFORMANCE OF THIS SOFTWARE.  >>USE AT YOUR OWN RISK.
          !
          ! CREATION DATE:    03-DEC-1990
          !
          ! MODIFICATION HISTORY:
          !
          !   03-DEC-1990 V1.0 Madison     Initial coding.
          !   11-MAR-1992 V1.1 Madison     Update for MX V3.1.
          !   15-MAY-1992 V1.2 Madison     Correct "restat" typo.  Add full_convert.
          !--
          */

          #include descrip
          #include string
          #include stdio
          #include ssdef
          #include str$routines
          #include lib$routines

          #define NICK_TO_ADDRESS     1
          #define USERNAME_TO_NICK    2

          #define NAME_COUNT       2

          static char *user [] = {"SMYTHE", "SYSTEM"};
          static char *nick [] = {"J.Smythe", "System.Manager"};

          #define FULL_COUNT       2

          static char *full_user[] = {"MADISON", "SHANDY_P"};
          static char *full_nick[] = {"madison@tgv.com",
                          "Peter_Shandy@portulaca-purple-passion.balaclava.edu"};

          C-2

 


                                          Name Conversion Example





          struct context {
              struct dsc$descriptor localnode;
              };

          /*
          !++
          !
          ! ROUTINE NAME:     INIT
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Allocates and initializes context block for subsequent name conversions.
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   INIT  ctxptr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */
          unsigned int
          init (struct context **ctx) {

              int ctxsize;
              $DESCRIPTOR(mx_node_name,"MX_NODE_NAME");

                                                              C-3

 


          Name Conversion Example





              ctxsize = sizeof(struct context);
              lib$get_vm (&ctxsize, ctx);
              (*ctx)->localnode.dsc$b_dtype = DSC$K_DTYPE_T;
              (*ctx)->localnode.dsc$b_class = DSC$K_CLASS_D;
              (*ctx)->localnode.dsc$w_length = 0;
              (*ctx)->localnode.dsc$a_pointer = NULL;
              lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode);
              return SS$_NORMAL;
          }  /* init */

          /*
          !++
          !
          ! ROUTINE NAME:     CONVERT
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Converts username -> nickname or nickname -> RFC821-address.
          !
          !   NB: You MUST use STR$ routines to copy result to OUTSTR parameter
          !       to ensure proper operation!!!!
          !
          !       You _may_ safely assume that INSTR is compatible with a DTYPE_T,
          !    CLASS_S (standard fixed-length) string descriptor.
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   CONVERT  ctxptr, code, instr, outstr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          ! code:     longword_unsigned, longword (unsigned), read only, by reference
          ! instr:    char_string, character string, read only, by descriptor (fixed)
          ! outstr:   char_string, character string, write only, by descriptor
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !

          C-4

 


                                          Name Conversion Example





          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */
          unsigned int
          convert (struct context **ctx, int *code, struct dsc$descriptor *instr,
                   struct dsc$descriptor *outstr) {

              struct dsc$descriptor tmp, tmp2;
              size_t count;
              int i, j, retstat;
              $DESCRIPTOR(lbrack, "<");
              $DESCRIPTOR(rbrack, ">");
              $DESCRIPTOR(atsign, "@");

              count = instr -> dsc$w_length;
              tmp.dsc$b_dtype = DSC$K_DTYPE_T;
              tmp.dsc$b_class = DSC$K_CLASS_D;
              tmp.dsc$w_length = 0;
              tmp.dsc$a_pointer = NULL;

              tmp2.dsc$b_dtype = DSC$K_DTYPE_T;
              tmp2.dsc$b_class = DSC$K_CLASS_S;












                                                              C-5

 


          Name Conversion Example





              switch (*code) {
          /*
          !++
          !  Local alias -> address
          !
          !   This code should return a status of SS$_NORMAL if an alias is found,
          !   0 otherwise.
          !
          !   If an alias is found, the resulting string MUST BE IN RFC821 format:
          !
          !                   <user@host>
          !
          !   >>>>>> EVEN IF THE ADDRESS IS FOR THE LOCAL HOST (so you have to
          !   look up MX_NODE_NAME and tack it on after the translated name,
          !   if you're just doing a local-host user directory).
          !--
          */
               case NICK_TO_ADDRESS:
                   retstat = 0;
                   str$copy_dx(&tmp, instr);
                   for (i = 0; i < NAME_COUNT; i++) {
                    tmp2.dsc$w_length = strlen(nick[i]);
                    tmp2.dsc$a_pointer = nick[i];
                    if (str$case_blind_compare(instr, &tmp2) == 0) {
                        j = strlen(user[i]);
                        str$copy_r(&tmp, &j, user[i]);
                        str$concat(outstr, &lbrack, &tmp, &atsign,
                         &(*ctx)->localnode, &rbrack);
                        retstat = SS$_NORMAL;
                        break;
                    }
                   }
                   break;







          C-6

 


                                          Name Conversion Example





          /*
          !++
          !   Username -> Alias
          !
          !   Return sucess status ONLY if you are actually converting the
          !   username to an alias!  Otherwise, return a non-success status code.
          !
          !   For compatibility with the name_conversion interface prior to
          !   MX V3.1, you should copy the input string to the output string
          !   when you return a non-success status.
          !
          !--
          */
               case USERNAME_TO_NICK:
                   retstat = 0;
                   str$copy_dx(outstr, instr);   /* for pre-V3.1 compatibility */
                   for (i = 0; i < NAME_COUNT; i++) {
                    tmp2.dsc$w_length = strlen(user[i]);
                    tmp2.dsc$a_pointer = user[i];
                    if (str$case_blind_compare(instr, &tmp2) == 0) {
                        j = strlen(nick[i]);
                        str$copy_r(outstr, &j, nick[i]);
                        retstat = SS$_NORMAL;
                        break;
                    }
                   }
                   break;
              }

              return retstat;

          }  /* convert */








                                                              C-7

 


          Name Conversion Example





          /*
          !++
          !
          ! ROUTINE NAME:     FULL_CONVERT
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Converts username -> alias address (full address substitution)
          !
          !   Unlike the CONVERT routine, FULL_CONVERT converts a username
          !   to a complete RFC822-type address.  You must be running MX V3.1C
          !   or later to use this feature.
          !
          !   NB: You MUST use STR$ routines to copy result to OUTSTR parameter
          !       to ensure proper operation!!!!
          !
          !       You _may_ safely assume that INSTR is compatible with a DTYPE_T,
          !    CLASS_S (standard fixed-length) string descriptor.
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   FULL_CONVERT  ctxptr, code, instr, outstr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          ! code:     longword_unsigned, longword (unsigned), read only, by reference
          ! instr:    char_string, character string, read only, by descriptor (fixed)
          ! outstr:   char_string, character string, write only, by descriptor
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !

          C-8

 


                                          Name Conversion Example





          !   None.
          !--
          */
          unsigned int
          full_convert (struct context **ctx, int *code, struct dsc$descriptor *instr,
                   struct dsc$descriptor *outstr) {

              struct dsc$descriptor tmp, tmp2;
              size_t count;
              int i, j, retstat;
              $DESCRIPTOR(lbrack, "<");
              $DESCRIPTOR(rbrack, ">");
              $DESCRIPTOR(atsign, "@");

              count = instr -> dsc$w_length;
              tmp.dsc$b_dtype = DSC$K_DTYPE_T;
              tmp.dsc$b_class = DSC$K_CLASS_D;
              tmp.dsc$w_length = 0;
              tmp.dsc$a_pointer = NULL;

              tmp2.dsc$b_dtype = DSC$K_DTYPE_T;
              tmp2.dsc$b_class = DSC$K_CLASS_S;

              if (*code != USERNAME_TO_NICK) return 0;
















                                                              C-9

 


          Name Conversion Example





          /*
          !++
          !   Username -> alias (full address conversion)
          !
          !   Return sucess status ONLY if you are actually converting the
          !   username to an alias!  Otherwise, return a non-success status code.
          !
          !--
          */
              retstat = 0;
              for (i = 0; i < FULL_COUNT; i++) {
               tmp2.dsc$w_length = strlen(full_user[i]);
               tmp2.dsc$a_pointer = full_user[i];
               if (str$case_blind_compare(instr, &tmp2) == 0) {
                   j = strlen(full_nick[i]);
                   str$copy_r(outstr, &j, full_nick[i]);
                   retstat = SS$_NORMAL;
                   break;
                   }
               }

              return retstat;

          }  /* full_convert */
















          C-10

 


                                          Name Conversion Example





          /*
          !++
          !
          ! ROUTINE NAME:     CLEANUP
          !
          ! FUNCTIONAL DESCRIPTION:
          !
          !   Deallocates context block allocated by init routine.
          !
          ! RETURNS:   cond_value, longword (unsigned), write only, by value
          !
          ! PROTOTYPE:
          !
          !   CLEANUP  ctxptr
          !
          ! ctxptr:   pointer, longword (unsigned), modify, by reference
          !
          ! IMPLICIT INPUTS:  None.
          !
          ! IMPLICIT OUTPUTS: None.
          !
          ! COMPLETION CODES:
          !
          !   SS$_NORMAL:      normal successful completion.
          !
          ! SIDE EFFECTS:
          !
          !   None.
          !--
          */
          unsigned int
          cleanup (struct context **ctx) {

              int ctxsize;

              str$free1_dx(&(*ctx)->localnode);
              ctxsize = sizeof(struct context);
              lib$free_vm (&ctxsize, ctx);
              *ctx = NULL;
              return SS$_NORMAL;
          }  /* cleanup */

                                                             C-11
