V'0);2Message Exchange Programmer's Guide;

Message Exchange Programmer's Guide





*

October, 1998



@This manual describes how to customize Message Exchange through programming.

.Revision/Update Information: This is a revised manual.

.Operating System and Version:VAX/VMS V5.2 or later

OpenVMS Alpha V1.5 or later

"Software Version:Message Exchange V5.1


17 October 1998

EThe information in this document is subject to change without notice Aand should not be construed as a commitment by MadGoat Software. CMadGoat Software assumes no responsibility for any errors that may appear in this document.

<No part of this publication may be reproduced, transmitted, Btranscribed, stored in a retrieval system, or translated into any Glanguage or computer language, in any form or by any means electronic, Hmechanical, magnetic, optical, chemical, or otherwise without the prior 'written permission of MadGoat Software.

CUse of this software and documentation is subject to the terms and .conditions set forth in the License Agreement.

AThe Licensed Materials are provided with RESTRICTED RIGHTS. Use, Hduplication, or disclosure by the Government is subject to restrictions Has set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data <and Computer Software clause at DFARS §252.227-7013 or 8subparagraphs (c)(1) and (2) of the Commercial Computer FSoftware---Restricted Rights at 48 CFR §52.227-19, as applicable.

EMadGoat, Message Exchange, and MX are trademarks of MadGoat Software.

>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.

CMultiNet and TCPware are registered trademarks of Process Software Corporation.

;LISTSERV is a registered trademark of L-Soft International.

AWIN/TCP and Pathway are registered trademarks of Attachmate, Inc.

Copyright ©1998



 ,  
GContents
 


$

Preface



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

4

Intended Audience



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

Document Structure



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

Related Documents



?You can find additional information in the following documents:

 


J

Chapter 1
The SITE Transport Interface




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

1.1 The SITE Delivery Interface



CWhen you use the MCP DEFINE PATH command to route mail to the SITE Hpath, the MX_SITE delivery agent takes messages routed to that path and Hfeeds them into a subprocess that executes a command procedure you must Fprovide. The command procedure must be called MX_EXE:SITE_DELIVER.COM and must accept four parameters.

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

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

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

 F  
Example 1-1 Sample SITE_DELIVER.COM
 

"
L$! Simple SITE_DELIVER.COM which invokes a real program to do all the work. K$! This file must be placed in MX_EXE: for use with the MX SITE interface. "$! It is invoked by MX_SITE with: $! L$!  @MX_EXE:SITE_DELIVER  route msg-file-spec dest-file-spec origin-address $! B$! The originator address is stuck in a file since it can contain J$! characters that might confuse DCL when we invoke the delivery program. $! G$! This is a simple procedure which ignores the "route" parameter.  If N$! 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 


 S

1.2 SITE Message Entry



EThe SITE message entry program should be used by your SITE transport Cagent to enter a message into the MX message queue. The program is Gcalled 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] 




EMX_SITE_IN takes up to three parameters, which correspond exactly to Hthe last three parameters passed out by the MX_SITE delivery agent. The :first parameter should be the name of a file containing a Fproperly-formatted RFC822 message. The second parameter should be the Fname of a file containing a list of RFC822 route addresses (they must Fhave the surrounding angle brackets, just as in an SMTP transaction). CThe third parameter, which is optional, should be the RFC822 route Gaddress of the sender (also including the surrounding angle brackets). GIf the third parameter is omitted, the address of the user running the 4program will be used as the origin of the message. 


L

Chapter 2
Address Modification Interface




?MX provides an interface for altering envelope addresses. This Ainterface allows you to add your own routines for performing two ;different types of address modifications. For each type of Fmodification, 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.

AThe address modification routines are located through the use of logical names. ' ,              
Logical name Modification type
 MX_SITE_ADDRESS_REWRITER M Modifying headers and envelope addresses for outgoing and incoming mail
 MX_SITE_DOM_EXPANSION ' Modifying or expanding host names
 MX_SITE_NAME_CONVERSION ? Translating local aliases or performing directory lookups


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

GExamples of routines for performing address modifications are included Din the directory MX_ROOT:[EXAMPLES] (if they have been installed). R

2.1 Address Rewriting



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

wThe name conversion routines, described in Section 2.3, provide a means Dfor implementing user aliases, but it does not affect domain names. FWith the address rewriter routines, both usernames and host names can Bbe modified. To ease mail delivery, many sites prefer to hide the Hvarious machines used at that site by supplying a generic site name for Gthe 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.

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

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


?

INIT



Initialization routine.



Format



INIT context




RETURNS

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


EThe 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

HThis is a longword passed by reference to your routine that you may use Bfor 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 GREWRITE_HEADER and REWRITE_ENVELOPE routines. You may use this routine Bto set up any context or perform any housekeeping tasks needed to ;prepare for the subsequent calls to the REWRITE_* routines.

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

"
I

REWRITE_HEADER



2Routine 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


BTo indicate a successful rewrite, return SS$_NORMAL or some other Esuccess status code. If you do not return a success status code, the 2caller 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.

outaddr


                
 VMS usage:  char_string
type:  character string
access:  write only
 mechanism:  by descriptor

GA string into which your routine should copy the rewritten address, if Fexpansion was successful. You must use the STR$ string routines (such 6as STR$COPY_DX) to copy the string into this argument.

header_code


                
 VMS usage:
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-Reply-To:  MX_K_HDR_R_REPLY_TO  19
 Resent-From:  MX_K_HDR_R_FROM  20
 Resent-Sender:  MX_K_HDR_R_SENDER  21


EThe symbolic names are defined in MX_HDR.H in MX_ROOT:[EXAMPLES], if you installed the examples.




DESCRIPTION

DThis routine is called to rewrite an address appearing in an RFC822 Fheader on all outgoing mail, regardless of its origin. The address of Fthe context block you allocated in the INIT routine is passed in here Bfor any information you need to keep track of between calls. This Broutine may be called more than once between one pair of INIT and CLEANUP calls.
"
K

REWRITE_ENVELOPE



.Routine to rewrite an RFC821 envelope address.



Format

3

REWRITE_ENVELOPE context, inaddr, outaddr




RETURNS

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


BTo indicate a successful rewrite, return SS$_NORMAL or some other Esuccess status code. If you do not return a success status code, the 2caller 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)

BThe RFC821 envelope address to be rewritten. RFC821 addresses are 4enclosed in angle brackets (<>). For example, H"<GENE@MADGOAT.COM>" is a valid RFC821 envelope address.

outaddr


                
 VMS usage:  char_string
type:  character string
access:  write only
 mechanism:  by descriptor

GA string into which your routine should copy the rewritten address, if Fexpansion was successful. You must use the STR$ string routines (such 6as 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 Bincoming mail. Envelope addresses are the addresses of the actual Frecipients of incoming mail and may or may not correspond directly to the RFC822 headers.

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

$
B

CLEANUP



Context cleanup routine.



Format



CLEANUP context




RETURNS

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


DThis routine should return a status value indicating the success or Gfailure 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

CThe address of the context block you allocated in the INIT routine.



DESCRIPTION

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

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



 T

2.2 Host Name Expansion



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

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

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

iA 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.


?

INIT



Initialization routine.



Format



INIT context




RETURNS

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


EThe 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

HThis is a longword passed by reference to your routine that you may use Bfor any purpose, such as allocating a block of memory for keeping contextual information.



DESCRIPTION

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

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


A

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


DTo indicate a successful expansion, return SS$_NORMAL or some other Esuccess status code. If you do not return a success status code, the 0caller 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.

expname


                
 VMS usage:  char_string
type:  character string
access:  write only
 mechanism:  by descriptor

HA string into which your routine should copy the expanded host name, if Fexpansion was successful. You must use the STR$ string routines (such 6as STR$COPY_DX) to copy the string into this argument.



DESCRIPTION

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

B

CLEANUP



Context cleanup routine.



Format



CLEANUP context




RETURNS

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


DThis routine should return a status value indicating the success or Gfailure 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

CThe address of the context block you allocated in the INIT routine.



DESCRIPTION

GThis routine is called to clean up after a series of EXPAND calls. You Cshould clean up the context information and deallocate the context $block allocated by the INIT routine.

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



 P

2.3 Name Conversion



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

nA sample name conversion module is provided in Appendix C.

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

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


?

INIT



Initialization routine.



Format



INIT context




RETURNS

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


EThe 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

HThis is a longword passed by reference to your routine that you may use Bfor any purpose, such as allocating a block of memory for keeping contextual information.



DESCRIPTION

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

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


B

CONVERT



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



Format

0

CONVERT context, code, inname, outname




RETURNS

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


BOn successful conversion, return SS$_NORMAL or some other success Dstatus 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

FThis 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

FA string into which your routine should copy the result. This is only )used if you return a success status code.



DESCRIPTION

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

   

"
<username@hostname> 



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

BFor username-to-alias conversion, the username to be converted is Bpassed in inname. If no conversion is performed, Areturn a non-success status code; otherwise, provide a result in Foutname. The result should be only the local part of Fan address; no host name should be appended nor any punctuation added.

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


C

CLEANUP



Context cleanup routine.



Format



CLEANUP context




RETURNS

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


DThis routine should return a status value indicating the success or Gfailure 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

CThe address of the context block you allocated in the INIT routine.



DESCRIPTION

HThis routine is called to clean up after a series of CONVERT calls. You Cshould clean up the context information and deallocate the context $block allocated by the INIT routine.

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


H

FULL_CONVERT



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



Format

5

FULL_CONVERT context, code, inname, outname




RETURNS

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


BOn successful conversion, return SS$_NORMAL or some other success Dstatus 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

HThis 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

FA 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 6conversion. The username to be converted is passed in Ainname. If no conversion is performed, return a Dnon-success status code. Unlike the CONVERT routine, the result you ?provide in outname on a successful conversion Gmust be a full RFC822-type address (user@host format).

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



 


H

Appendix A
Address Rewriter Example




BThis 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 ! 6! AUTHOR: Andrew Greer <Andrew.Greer@vuw.ac.nz> 4!  Hunter Goatley <goathunter@WKUVX1.WKU.EDU> =!  Copyright © 1994, MadGoat Software.  All rights reserved. ! ! MODULE DESCRIPTION: ! J!   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 9! UNIVERSAL=INIT,REWRITE_HEADER,REWRITE_ENVELOPE,CLEANUP ! ^Z ! $ ! !   On AXP, use: ! ! $ CC ADDRESS_REWRITER ?! $ LINK/NOTRACE/SHARE ADDRESS_REWRITER.OBJ, SYS$INPUT:/OPTION ! SYMBOL_VECTOR=(- !    INIT   = PROCEDURE,- "!    REWRITE_HEADER = PROCEDURE,- $!    REWRITE_ENVELOPE = PROCEDURE,- !    CLEANUP  = PROCEDURE) ! ^Z ! $ ! I!   Then copy it to MX_EXE: and make it available to the Router with the !   following commands: ! ,!       $ COPY ADDRESS_REWRITER.EXE MX_EXE: N!       $ DEFINE/SYSTEM/EXEC MX_SITE_ADDRESS_REWRITER MX_EXE:ADDRESS_REWRITER !       $ MCP RESET ROUTER !  !   Format of the file used is: ! ! USERNAME ALIAS ! K!   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 ! O! A lot of this is stolen directory from the NAME_CONVERSION routines provided ,! as an example of CONVERT and FULL_CONVERT ! L! Basically rewrite the FROM/RESENT_FROM headers to match the Email address N! format we use. Also rewrite the Envelope so that the mail gets delivered to 0! the username that matches that Email address. ! H! E.g. mail from andrew@matai.vuw.ac.nz will get the From: rewritten as !   Andrew.Greer@vuw.ac.nz D! And mail coming into Andrew.Greer@vuw.ac.nz will get delivered to ! andrew@matai.vuw.ac.nz ! +!  The following logicals must be defined: ! 7! 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) 3! 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 */  >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 */  Kstatic 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; }   Dint check_name(struct context **ctx, struct dsc$descriptor *inname, /               struct dsc$descriptor *outname) {   int idx;   char user[64];    struct dsc$descriptor tmpdsc;  E  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]);   }  5  for(idx=0; idx < ((*ctx)->num_names); idx++)    { 5    if (!strcmp ((*ctx)->names[idx].alias, user))      { U      init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].user); )      str$copy_dx(outname, &tmpdsc);       return SS$_NORMAL;      }    } /* for */    return 0;  } /* check_name */   Cint get_alias(struct context **ctx, struct dsc$descriptor *inname, /               struct dsc$descriptor *outname) {   int idx;   char user[64];    struct dsc$descriptor tmpdsc;  D  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]);  3  for(idx=0; idx < (*ctx)->num_names; idx++)    { 3    if (!strcmp((*ctx)->names[idx].user, user))      { T      init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].ret); )      str$copy_dx(outname, &tmpdsc);       return SS$_NORMAL;      }    } /* for */    return 0;  } /* get_alias */  /* !++ ! ! ROUTINE NAME:     INIT ! ! FUNCTIONAL DESCRIPTION: ! M!   Allocates and initializes context block for subsequent name conversions. ! F! 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: ! 6!   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"); 5    $DESCRIPTOR(mx_site_generic, "MX_SITE_GENERIC"); 9    $DESCRIPTOR(mx_site_aliases, "MX_SITE_ALIASES_TMP");     char alias_file[64]; $    struct dsc$descriptor aliasdsc;     FILE *fd;  &    ctxsize = sizeof(struct context); -    status = lib$get_vm (&ctxsize, ctx); -    if (status != SS$_NORMAL) return status;  8    init_dynamic_descriptor(&(*ctx)->localnode); 6    init_dynamic_descriptor(&(*ctx)->generic); ,    init_dynamic_descriptor(&aliasdsc);  O    status = lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode); -    if (status != SS$_NORMAL) return status;  P    status = lib$sys_trnlog (&mx_site_generic, 0, &(*ctx)->generic); -    if (status != SS$_NORMAL) return status;  F    status = lib$sys_trnlog (&mx_site_aliases, 0, &aliasdsc); -    if (status != SS$_NORMAL) return status;  H    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; 8      for (idx=0; idx < (*ctx)->num_names; idx++) 	       { =        fscanf(fd, "%s %s", &(*ctx)->names[idx].user, #  &(*ctx)->names[idx].ret);  9 /* Convert the alias to lowercase for matching later */ D 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;          return SS$_NORMAL; }  /* init */  /* !++ ! #! ROUTINE NAME:     REWRITE_HEADER ! ! FUNCTIONAL DESCRIPTION: ! F! RETURNS:      cond_value, longword (unsigned), write only, by value ! 
! PROTOTYPE: ! /!   REWRITE_HEADER ctxptr, instr, outstr, code ! ?! ctxptr:   pointer, longword (unsigned), modify, by reference L! instr:    char_string, character string, read only, by descriptor (fixed) E! outstr:   char_string, character string, write only, by descriptor K! code:     word_unsigned, word (unsigned), read only, by value/reference? ! ! IMPLICIT INPUTS:  None. ! ! IMPLICIT OUTPUTS: None. ! ! COMPLETION CODES: ! 6!   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 ) {   int   rc, 
        len, 
        pos,         start_pos,         end_pos; 7  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; F    str$len_extr(&localdsc, inadr, &start_pos, &end_pos);     start_pos = pos + 1; ,    end_pos = inadr->dsc$w_length - pos; D    str$len_extr(&domdsc, inadr, &start_pos, &end_pos);     }    switch (code)    { Q    /* Possible headers that could be rewritten (from [MX.ROUTER]PROCESS.B32) */ /    case MX_K_HDR_FROM:             /* From */ 6    case MX_K_HDR_R_FROM:           /* Resent From */ Q            rc = str$case_blind_compare(&domdsc, &(*ctx)->localnode); A            if (get_alias(ctx, &localdsc, &newlocaldsc))              { Z              str$concat(outadr, &newlocaldsc, &atsign, &(*ctx)->generic); !              return SS$_NORMAL;              }             break; -    case MX_K_HDR_TO:               /* To */ 4    case MX_K_HDR_R_TO:             /* Resent To */ -    case MX_K_HDR_CC:               /* CC */ 4    case MX_K_HDR_R_CC:             /* Resent CC */ .    case MX_K_HDR_BCC:              /* BCC */ 5    case MX_K_HDR_R_BCC:            /* Resent BCC */ 3    case MX_K_HDR_REPLY_TO:         /* Reply To */ 1    case MX_K_HDR_SENDER:           /* Sender */ 8    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: ! F! RETURNS:      cond_value, longword (unsigned), write only, by value ! 
! PROTOTYPE: ! +!   REWRITE_ENVELOPE ctxptr, inadr, outadr ! ?! ctxptr:   pointer, longword (unsigned), modify, by reference L! instr:    char_string, character string, read only, by descriptor (fixed) E! outstr:   char_string, character string, write only, by descriptor ! ! IMPLICIT INPUTS:  None. ! ! IMPLICIT OUTPUTS: None. ! ! COMPLETION CODES: ! 6!   SS$_NORMAL:         normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- */ 
unsigned int (rewrite_envelope( struct context **ctx, 0                  struct dsc$descriptor *inadr, 2                  struct dsc$descriptor *outadr ) {   int   rc, 
        len, 
        pos,         start_pos,         end_pos; 7  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 ">" */ 6    start_pos = str$position(inadr, &lbrack) + 1;     end_pos = pos - 2; F    str$len_extr(&localdsc, inadr, &start_pos, &end_pos);     start_pos = pos + 1; 0    end_pos = str$position(inadr, &rbrack);     if (end_pos > 0) %      end_pos = end_pos - start_pos; 	    else .      end_pos = inadr->dsc$w_length - pos; D    str$len_extr(&domdsc, inadr, &start_pos, &end_pos);    }  E  rc = str$case_blind_compare(&domdsc, &(*ctx)->generic);   if (rc != 0)     return 0;  8  if (check_name(ctx, &localdsc, &newlocaldsc))    { ^    str$concat(outadr, &lbrack, &newlocaldsc, &atsign, &(*ctx)->localnode,             &rbrack);     return SS$_NORMAL;    }    return 0;  } /* rewrite_envelope */  /* */ 
unsigned int !cleanup (struct context **ctx) {      int ctxsize, status;  6    status = str$free1_dx(&(*ctx)->localnode); -    if (status != SS$_NORMAL) return status;  4    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); K   status = rewrite_header (&ctx, &x, &outstr, MX_K_HDR_FROM); !   lib$put_output (&outstr); >   status = rewrite_envelope (&ctx, &y, &outstr); !   lib$put_output (&outstr);     return(SS$_NORMAL); } #endif 


 


H

Appendix B
Domain Expansion Example




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

 

"
%TITLE 'DOM_EXPANSION_CMU' NMODULE DOM_EXPANSION_CMU (IDENT='V1.0', ADDRESSING_MODE (EXTERNAL=GENERAL)) = BEGIN !++ ! FACILITY:      MX Examples ! D! ABSTRACT:      Example of a domain name expander for use with MX. +!             For use with CMU-Tek TCP/IP. ! ! MODULE DESCRIPTION: ! A!   This module contains the routines necessary for implementing B!   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 0!   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 I!    $ LINK/SHARE=DOM_EXPANSION/NOTRACE DOM_EXPANSION_CMU,SYS$INPUT:/OPT '!        UNIVERSAL=INIT,EXPAND,CLEANUP !        <ctrl/Z> ! D!   Then copy it to MX_EXE and make it available to the Router with !   the commands: ! &!    $ COPY DOM_EXPANSION.EXE MX_EXE: E!    $ DEFINE/SYSTEM/EXEC MX_SITE_DOM_EXPANSION MX_EXE:DOM_EXPANSION !    $ MCP RESET ROUTER ! 9!   (You need a suitably privileged account to do this.) ! ! AUTHOR:        M. Madison F!      Copyright © 1993,1994, MadGoat Software.  All Rights Reserved. !  ! CREATION DATE:    07-DEC-1990 ! ! MODIFICATION HISTORY: ! 1!   07-DEC-1990 V1.0 Madison     Initial coding. !-- #    LIBRARY 'SYS$LIBRARY:STARLET'; #    LIBRARY 'SYS$LIBRARY:NETWORK';      EXTERNAL ROUTINE 7     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)%;  %SBTTL 'INIT'  GLOBAL ROUTINE INIT (CTX_A_A) = BEGIN !++ ! FUNCTIONAL DESCRIPTION: ! E!   Called by the Router to initialize the module.  Could be used to C!   allocate any storage that will be needed by the EXPAND routine E!   (these routines must be reentrant, so OWN storage is right out). ! C! 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: ! 3!   SS$_NORMAL:      normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- 	    BIND %     CTX  = .CTX_A_A   : REF CTXDEF;  
    LOCAL 
     STATUS;  4    STATUS = LIB$GET_VM (%REF (CTX_S_CTXDEF), CTX);     IF .STATUS THEN 
    BEGIN C     STATUS = $ASSIGN (DEVNAM=%ASCID'IP0', CHAN=CTX [CTX_W_CHAN]); A     IF NOT .STATUS THEN LIB$FREE_VM (%REF (CTX_S_CTXDEF), CTX); 	    END;      .STATUS  END; ! INIT  %SBTTL 'EXPAND' 5GLOBAL ROUTINE EXPAND (CTX_A_A, INSTR_A, OUTSTR_A) = BEGIN !++ ! FUNCTIONAL DESCRIPTION: ! ?!   This routine is called to perform a domain name expansion. ! D!   INSTR can be assumed to be a DTYPE_T, CLASS_S string descriptor D!   (or compatible).  You must use STR$ routines to copy the result !   to OUTSTR! ! C! RETURNS:   cond_value, longword (unsigned), write only, by value ! 
! PROTOTYPE: ! "!   EXPAND  ctxptr, instr, outstr ! ?! ctxptr:   pointer, longword (unsigned), modify, by reference D! instr:    char_string, character string, read only, by descriptor E! outstr:   char_string, character string, write only, by descriptor ! ! IMPLICIT INPUTS:  None. ! ! IMPLICIT OUTPUTS: None. ! ! COMPLETION CODES: ! 3!   SS$_NORMAL:      normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- 	    BIND )     CTX  = .CTX_A_A       : REF CTXDEF, &     CHN  = CTX [CTX_W_CHAN]  : WORD, 8     INSTR = .INSTR_A       : BLOCK [DSC$K_S_BLN,BYTE], 9     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); 8    STR$CONCAT (STR, INSTR, %ASCID %STRING(%CHAR (0))); D    STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK), C     GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,      IO$SB=IOSB); E    IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THEN ?                    .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]);     IF NOT .STATUS AND M         CH$RCHAR (.INSTR [DSC$A_POINTER]+.INSTR [DSC$W_LENGTH]-1) NEQ %C'.' 	    THEN 
    BEGIN ?     STR$CONCAT (STR, INSTR, %ASCID %STRING ('.', %CHAR (0))); E     STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK), G         GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,          IO$SB=IOSB); F     IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THEN ?                    .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]); 	    END;     STR$FREE1_DX (STR); G    IF .STATUS THEN STATUS = STR$COPY_R (OUTSTR, GHBLK [GH$NL_NAMLEN], /                        GHBLK [GH$NL_NAMSTR]);     .STATUS  END; ! EXPAND  %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. ! C! 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: ! 3!   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 


 !


G

Appendix C
Name Conversion Example




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

 

"
/* !++ ! !! MODULE:        NAME_CONVERSION ! ! FACILITY:      MX examples ! B! ABSTRACT:      Example of site-installable nickname conversion. ! ! MODULE DESCRIPTION: ! G!   This module contains routines for use by MX modules (specifically, J!   the MX_MAILSHR interface to VMS Mail and the MX_ROUTER agent process) L!   for translating between actual VMS usernames and site-specific aliases. ! F!   This module contains a fairly primitive lookup table to implement !   the translation. ! K!   To use this module: MODIFY IT AS NEEDED FOR YOUR SITE, then compile it #!   and link it with the commands: ! !       $ cc name_conversion 9!    $ link/share/notrace name_conversion,sys$input:/opt !!        sys$share:vaxcrtl/share 5!        universal=init,convert,full_convert,cleanup !        <ctrl/Z> ! D!   Then copy it to MX_EXE and make it available with the commands: ! 8!    $ copy name_conversion.exe mx_exe:/protection=w:re ?!    $ install create mx_exe:name_conversion/share/open/header I!    $ define/system/exec mx_site_name_conversion mx_exe:name_conversion <!    $ mcp reset router  ! to force Router to load the code ! 9!   (You need a suitably privileged account to do this.) ! ! AUTHOR:        M. Madison F!      Copyright © 1993,1994, MadGoat Software.  All Rights Reserved. ! F!   THIS SOFTWARE IS PROVIDE "AS IS".  NEITHER THE AUTHOR NOR MadGoat J!   MAKE ANY GUARANTEES REGARDING THE SUITABILITY, RELIABILITY, SECURITY, P!   USEFULNESS, OR PERFORMANCE OF THIS SOFTWARE.  >>USE AT YOUR OWN RISK. !  ! CREATION DATE:    03-DEC-1990 ! ! MODIFICATION HISTORY: ! 1!   03-DEC-1990 V1.0 Madison     Initial coding. 5!   11-MAR-1992 V1.1 Madison     Update for MX V3.1. K!   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"}; 7static char *nick [] = {"J.Smythe", "System.Manager"};  #define FULL_COUNT       2  4static char *full_user[] = {"MADISON", "SHANDY_P"}; /static char *full_nick[] = {"madison@tgv.com", H                "Peter_Shandy@portulaca-purple-passion.balaclava.edu"};  struct context { %    struct dsc$descriptor localnode;     };      /* !++ ! ! ROUTINE NAME:     INIT ! ! FUNCTIONAL DESCRIPTION: ! M!   Allocates and initializes context block for subsequent name conversions. ! C! 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: ! 3!   SS$_NORMAL:      normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- */ 
unsigned int init (struct context **ctx) {      int ctxsize; .    $DESCRIPTOR(mx_node_name,"MX_NODE_NAME");  &    ctxsize = sizeof(struct context); $    lib$get_vm (&ctxsize, ctx); 6    (*ctx)->localnode.dsc$b_dtype = DSC$K_DTYPE_T; 6    (*ctx)->localnode.dsc$b_class = DSC$K_CLASS_D; +    (*ctx)->localnode.dsc$w_length = 0; /    (*ctx)->localnode.dsc$a_pointer = NULL; F    lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode);     return SS$_NORMAL; }  /* init */  /* !++ ! ! ROUTINE NAME:     CONVERT ! ! FUNCTIONAL DESCRIPTION: ! G!   Converts username -> nickname or nickname -> RFC821-address. ! F!   NB: You MUST use STR$ routines to copy result to OUTSTR parameter '!       to ensure proper operation!!!! ! I!       You _may_ safely assume that INSTR is compatible with a DTYPE_T, 8!    CLASS_S (standard fixed-length) string descriptor. ! C! RETURNS:   cond_value, longword (unsigned), write only, by value ! 
! PROTOTYPE: ! )!   CONVERT  ctxptr, code, instr, outstr ! ?! ctxptr:   pointer, longword (unsigned), modify, by reference L! code:     longword_unsigned, longword (unsigned), read only, by reference L! instr:    char_string, character string, read only, by descriptor (fixed) E! outstr:   char_string, character string, write only, by descriptor ! ! IMPLICIT INPUTS:  None. ! ! IMPLICIT OUTPUTS: None. ! ! COMPLETION CODES: ! 3!   SS$_NORMAL:      normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- */ 
unsigned int Hconvert (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;      switch (*code) { /* !++ !  Local alias -> address ! I!   This code should return a status of SS$_NORMAL if an alias is found, !   0 otherwise. ! I!   If an alias is found, the resulting string MUST BE IN RFC821 format: ! &!                   <user@host> ! W!   >>>>>> EVEN IF THE ADDRESS IS FOR THE LOCAL HOST (so you have to C!   look up MX_NODE_NAME and tack it on after the translated name, 7!   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]); 5              str$copy_r(&tmp, &j, user[i]); E              str$concat(outstr, &lbrack, &tmp, &atsign, 8               &(*ctx)->localnode, &rbrack); $              retstat = SS$_NORMAL;               break;           }          }          break;  /* !++ !   Username -> Alias ! A!   Return sucess status ONLY if you are actually converting the H!   username to an alias!  Otherwise, return a non-success status code. ! B!   For compatibility with the name_conversion interface prior to C!   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; H         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]); 3              str$copy_r(outstr, &j, nick[i]); $              retstat = SS$_NORMAL;               break;           }          }          break;     }      return retstat;  }  /* convert */  /* !++ ! !! ROUTINE NAME:     FULL_CONVERT ! ! FUNCTIONAL DESCRIPTION: ! F!   Converts username -> alias address (full address substitution) ! A!   Unlike the CONVERT routine, FULL_CONVERT converts a username E!   to a complete RFC822-type address.  You must be running MX V3.1C "!   or later to use this feature. ! F!   NB: You MUST use STR$ routines to copy result to OUTSTR parameter '!       to ensure proper operation!!!! ! I!       You _may_ safely assume that INSTR is compatible with a DTYPE_T, 8!    CLASS_S (standard fixed-length) string descriptor. ! C! RETURNS:   cond_value, longword (unsigned), write only, by value ! 
! PROTOTYPE: ! .!   FULL_CONVERT  ctxptr, code, instr, outstr ! ?! ctxptr:   pointer, longword (unsigned), modify, by reference L! code:     longword_unsigned, longword (unsigned), read only, by reference L! instr:    char_string, character string, read only, by descriptor (fixed) E! outstr:   char_string, character string, write only, by descriptor ! ! IMPLICIT INPUTS:  None. ! ! IMPLICIT OUTPUTS: None. ! ! COMPLETION CODES: ! 3!   SS$_NORMAL:      normal successful completion. ! ! SIDE EFFECTS: ! 
!   None. !-- */ 
unsigned int Mfull_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;  /* !++ 3!   Username -> alias (full address conversion) ! A!   Return sucess status ONLY if you are actually converting the H!   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]); 3         str$copy_r(outstr, &j, full_nick[i]);          retstat = SS$_NORMAL;          break;          }      }      return retstat;  }  /* full_convert */  /* !++ ! ! ROUTINE NAME:     CLEANUP ! ! FUNCTIONAL DESCRIPTION: ! 9!   Deallocates context block allocated by init routine. ! C! 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: ! 3!   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 */ 




 .
^ Contents