/* **++ ** FACILITY: MX Examples ** ** ABSTRACT: Example of a message filter for use with MX. ** ** MODULE DESCRIPTION: ** ** This module contains routines for use by the MX Router for ** filtering messages based on envelope and header information. To ** build it, use: ** ** $ CC FILTER ** $ LINK/NOTRACE/SHARE/EXEC=MX_EXE:FILTERSHR.EXE FILTER.OBJ,SYS$INPUT:/OPT ** ** For VAX systems, enter the following linker options: ** SYS$LIBRARY:VAXCRTL/SHARE (only if using VAX C, not DEC C) ** UNIVERSAL=INIT,FILTER,FINISH ** For Alpha systems, enter these options instead: ** SYMBOL_VECTOR=(INIT=PROCEDURE,FILTER=PROCEDURE,FINISH=PROCEDURE) ** ** Once linked, define the following logical name: ** ** $ DEFINE/SYSTEM/EXEC MX_SITE_MESSAGE_FILTER MX_EXE:FILTERSHR.EXE ** ** Then stop and restart or reset the MX Router: ** ** $ MCP RESET ROUTER ** ** -- You should NOT use the C run-time memory-allocation routines ** (e.g., malloc and free) to allocate dynamic memory here. Please ** use LIB$GET_VM and LIB$FREE_VM instead. ** ** ------------- ** PLEASE NOTE: This is an UNDOCUMENTED and UNSUPPORTED interface to ** MX that is subject to change in future versions. ** ------------- ** ** AUTHOR: M. Madison Copyright (c) 2008, Matthew Madison. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright owner nor the names of any other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** CREATION DATE: 08-SEP-1997 ** ** MODIFICATION HISTORY: ** ** 08-SEP-1997 V1.0 Madison Initial coding. **-- */ #include #include #include #include #include #include #include "mx_filterdef.h" /* ** Item lists are used to convey information */ #pragma member_alignment __save #pragma nomember_alignment typedef struct item_list_3 { unsigned short bufsiz, itmcod; void *bufadr, *retlen; } ITMLST3; /* ** The returned-information item list structure ** uses the third longword a little differently ** (and only for header modifications). */ typedef struct return_item_list_3 { unsigned short bufsiz, itmcod; void *bufadr; unsigned short preference, tagnum; } RITMLST3; #pragma member_alignment __restore /* ** Context structure for tracking memory we allocate */ typedef struct { unsigned int rcptbufsize; RITMLST3 *rcptbuf; unsigned int hdrbufsize; RITMLST3 *hdrbuf; } CONTEXT; static unsigned int CONTEXT_SIZE = sizeof(CONTEXT); /* ** Forward declarations */ unsigned int INIT(CONTEXT **contxt); unsigned int FILTER(CONTEXT **contxt, ITMLST3 *envlst, ITMLST3 *hdrlst, struct dsc$descriptor *msgfile, RITMLST3 **addrcpts, RITMLST3 **addhdrs); unsigned int FINISH(CONTEXT **contxt); /* **++ ** ROUTINE: INIT ** ** FUNCTIONAL DESCRIPTION: ** ** Initializes context for the message filter. ** ** RETURNS: condition value ** ** PROTOTYPE: ** ** INIT contxt ** ** contxt: user_arg, longword (unsigned), write only, by reference ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: See code. ** ** SIDE EFFECTS: None. ** **-- */ unsigned int INIT (CONTEXT **contxt) { unsigned int status; status = LIB$GET_VM (&CONTEXT_SIZE, contxt); if ($VMS_STATUS_SUCCESS(status)) memset(*contxt, 0, CONTEXT_SIZE); return status; } /* INIT */ /* **++ ** ROUTINE: FILTER ** ** FUNCTIONAL DESCRIPTION: ** ** Runs the message filter. ** ** RETURNS: condition value ** ** PROTOTYPE: ** ** FILTER contxt, envlst, hdrlst, msgfile, addrcpt, addhdrs ** ** contxt: user_arg, longword (unsigned), modify, by reference ** envlst: item_list_3, longword (unsigned), read only, by reference ** hdrlst: item_list_3, longword (unsigned), read only, by reference ** msgfile: char_string, character string, read only, by descriptor ** addrcpt: address (of item_list_3), longword (unsigned), write only, by reference ** addhdrs: address (of item_list_3), longword (unsigned), write only, by reference ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: See code. ** ** SIDE EFFECTS: None. ** **-- */ unsigned int FILTER (CONTEXT **contxt, ITMLST3 *envlst, ITMLST3 *hdrlst, struct dsc$descriptor *msgfile, RITMLST3 **addrcpts, RITMLST3 **addhdrs) { ITMLST3 *itm; RITMLST3 *ritm; unsigned int message_size, message_source; int found_org; int sender_len; char *sender_ptr; static char rewrite_test_recip[] = ""; static char rewrite_target_recip[] = ""; static char delete_test_recip[] = ""; static char reject_test_recip[] = ""; static char reject_message[] = "This is a test rejection message."; static char bouncemaster[] = ""; static char organization[] = "X-Organization:"; static char my_organization[] = "X-Organization: International Union of Mail Hackers"; /* ** Run through the envelope itemlist. */ for (itm = envlst; !(itm->bufsiz == 0 && itm->itmcod == 0); itm++) { switch (itm->itmcod) { /* ** FLTR__ENV_MSGSIZE: size of message, in bytes (longword) ** This is provided for informational purposes only. */ case FLTR__ENV_MSGSIZE: message_size = *(unsigned int *)itm->bufadr; break; /* ** FLTR__ENV_SOURCE: code indicating message source (longword) ** This is provided for informational purposes only. See the ** FLTR_K_SOURCE_* constants in MX_FILTERDEF.H. */ case FLTR__ENV_SOURCE: /* This is read-only */ message_source = *(unsigned int *)itm->bufadr; break; /* ** FLTR__ENV_SENDER: address of sender (string) ** This item comes with a pointer to another ITMLST3 structure ** in the retlen field for returning information. You can rewrite ** the sender address by setting the itmcod field in the returned- ** information item and providing the new address in the bufsiz/ ** bufadr fields there. */ case FLTR__ENV_SENDER: sender_len = itm->bufsiz; sender_ptr = itm->bufadr; break; /* ** If we were to rewrite the sender, we would use: ** ** ritm = itm->retlen; ** ritm->itmcod = FLTR__REWRITE; ** ritm->bufsiz = length of new address ** ritm->bufadr = pointer to string containing new address ** ** It's important that the new address be surrounded by angle brackets ** just as the old address was. */ /* ** Each recipient is listed in the item list, and each comes with ** a returned-information ITMLST3 structure pointed at by the ** retlen field. Valid returned-information item codes are: ** FLTR__REWRITE to rewrite the address ** FLTR__REJECT to cause delivery to be rejected ** (with an error message returned to sender) ** FLTR__DELETE to cause delivery to be skipped, silently ** ** See the examples below. */ case FLTR__ENV_RECIPIENT: if (itm->bufsiz == sizeof(rewrite_test_recip)-1 && memcmp(itm->bufadr, rewrite_test_recip, itm->bufsiz) == 0) { ritm = itm->retlen; ritm->bufsiz = strlen(rewrite_target_recip); ritm->bufadr = rewrite_target_recip; ritm->itmcod = FLTR__REWRITE; } if (itm->bufsiz == sizeof(reject_test_recip)-1 && memcmp(itm->bufadr, reject_test_recip, itm->bufsiz) == 0) { ritm = itm->retlen; ritm->bufsiz = strlen(reject_message); /* If you do not specify a message here, */ ritm->bufadr = reject_message; /* a generic message will be used */ ritm->itmcod = FLTR__REJECT; } if (itm->bufsiz == sizeof(delete_test_recip)-1 && memcmp(itm->bufadr, delete_test_recip, itm->bufsiz) == 0) { ritm = itm->retlen; ritm->itmcod = FLTR__DELETE; } break; default: break; } /* switch */ } /* for */ /* ** Any additional recipients you might want to add should be provided in an ** item list (an array of ITMLST3 structures, with list termination indicated ** by bufsiz & itmcod fields equal to zero) that you allocate. Place the ** address of the itemlist in the _addrcpts_ argument. Example: */ if (sender_len == 2 && *sender_ptr == '<' && *(sender_ptr+1) == '>') { RITMLST3 *newlst; unsigned int newsize; newsize = 2 * sizeof(RITMLST3); if ($VMS_STATUS_SUCCESS (LIB$GET_VM (&newsize, &newlst))) { newlst[0].itmcod = 0; newlst[0].bufsiz = strlen(bouncemaster); newlst[0].bufadr = bouncemaster; newlst[0].preference = newlst[0].tagnum = 0; newlst[1].itmcod = newlst[1].bufsiz = 0; *addrcpts = newlst; (*contxt)->rcptbuf = newlst; /* Save pointer for freeing later */ (*contxt)->rcptbufsize = newsize; } } /* ** The header list is also provided as an item list, with a returned-information ** item pointed to by the retlen field of each item. You can use the returned-information ** item to delete, rewrite, and/or reorder the headers. ** ** Note that the bufadr/buflen fields point only to the TEXT of the header, and do ** not include the tag (e.g., "Subject:", "From:", etc.). The tag is represented ** numerically by the itmcod field. If the itmcod field is FLTR__HDR_OTHER, then ** the tag was not recognized by MX and the text DOES include the tag. ** ** Use the FLTR__DELETE and FLTR__REWRITE item codes (as above for recipients) to ** delete and modify headers. ** ** To delete a header, simply set the ritm->itmcod field to FLTR__DELETE. ** To modify a header, set ritm->itmcod to FLTR__REWRITE, set ritm->bufsiz ** to the length of the text of the header, and ritm->bufadr to the string ** representing the header text. Then set ritm->tagnum to the appropriate ** FLTR__HDR_* code. If the tag code is FLTR__HDR_OTHER, then the text ** _should_ include the header tag; otherwise, it _should not_. DO NOT ** cheat and use FLTR__HDR_OTHER to insert headers with tags recognized by ** MX -- use the appropriate code instead. ** ** To reorder the headers: by default, the headers will be written back ** in the order provided to you in the item list, with any additional headers ** you provide added to the end of the original list. If you need to have ** the headers provided in a certain order, you can set the ritm->preference ** code to specify the relative preference for each header (with the lowest ** preference headers coming first in the list). The preference code can ** be any arbitrary integer from zero to 32767, but it will be most efficient ** if you keep to a small range of integers. Multiple headers can share ** a single preference value; they will simply be replaced in the order found ** in this list. */ /* ** In this example, we'll add an X-Organization: header to the message ** if one is not already there. If one _is_ there already, we replace ** it with the one we want. This applies only to locally-originated ** messages, of course. ** ** MX does not have a header code for X-Organization:, so we look ** for FLTR__HDR_OTHER and do a string comparison. */ found_org = 0; if (message_source == FLTR_K_SOURCE_LOCAL) { for (itm = hdrlst; !(itm->bufsiz == 0 && itm->itmcod == 0); itm++) { if (itm->itmcod == FLTR__HDR_OTHER) { if (itm->bufsiz == sizeof(my_organization)-1 && /* don't replace it if */ memcmp(itm->bufadr, my_organization, itm->bufsiz) == 0) /* it's already correct */ found_org = 1; else if (itm->bufsiz >= sizeof(organization)-1 && memcmp(itm->bufadr, organization, sizeof(organization)-1) == 0) { /* replace it */ found_org = 1; ritm = itm->retlen; ritm->itmcod = FLTR__REWRITE; ritm->bufsiz = strlen(my_organization); ritm->bufadr = my_organization; ritm->tagnum = FLTR__HDR_OTHER; } } } if (!found_org) { /* none there, add it */ RITMLST3 *newlst; unsigned int newsize; newsize = 2 * sizeof(RITMLST3); if ($VMS_STATUS_SUCCESS (LIB$GET_VM (&newsize, &newlst))) { newlst[0].itmcod = FLTR__HDR_OTHER; /* note NORMAL place for this code here */ newlst[0].bufsiz = strlen(my_organization); newlst[0].bufadr = my_organization; newlst[0].preference = newlst[0].tagnum = 0; newlst[1].itmcod = newlst[1].bufsiz = 0; } *addhdrs = newlst; (*contxt)->hdrbuf = newlst; /* Save pointer for freeing later */ (*contxt)->hdrbufsize = newsize; } } return SS$_NORMAL; } /* FILTER */ /* **++ ** ROUTINE: FINISH ** ** FUNCTIONAL DESCRIPTION: ** ** Cleans up after a filter run. ** ** RETURNS: condition value ** ** PROTOTYPE: ** ** FINISH contxt ** ** contxt: user_arg, longword (unsigned), modify, by reference ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: See code. ** ** SIDE EFFECTS: None. ** **-- */ unsigned int FINISH (CONTEXT **contxt) { CONTEXT *c = *contxt; if (c->rcptbuf != 0) LIB$FREE_VM (&c->rcptbufsize, &c->rcptbuf); if (c->hdrbuf != 0) LIB$FREE_VM (&c->hdrbufsize, &c->hdrbuf); LIB$FREE_VM (&CONTEXT_SIZE, &c); *contxt = 0; return SS$_NORMAL; } /* FINISH */