/* ** File name: USER_SMTP_DISPATCH.C ** Edit level: 014 ** ** Copyright (c) 1998, 1999, by Process Software Corporation ** Copyright (c) 2000 - 2002 by Process Software ** Framingham, Massachusetts ** ** This software is furnished under a license for use on a ** single computer system and may be copied only with the ** inclusion of the above copyright notice. This software, or ** any other copies thereof, may not be provided or otherwise ** made available to any other person except for use on such ** system and to one who agrees to these license terms. Title ** to and ownership of the software shall at all times remain ** in Process Software's name. ** ** The information in this document is subject to change ** without notice and should not be construed as a commitment ** by Process Software. Process Software ** assumes no responsibility for any errors that ** may appear in this document. */ /* **++ ** FACILITY: SMTP ** ** ABSTRACT: Customization routines for PSC SMTP. ** ** MODULE DESCRIPTION: ** ** This is a template SMTP customization image. It provides the ** following callouts: ** ** find_gateway_v41 - for routing messages via a gateway ** system based on host names ** ** alias_lookup_v33 - for aliasing local mailbox names ** ** vrfy_lookup_v32 - for changing VRFY command processing ** in the SMTP server ** ** expn_lookup_v32 - for changing EXPN command processing ** in the SMTP server ** ** An exact copy of this code is linked into the MULTINET_SMTP_SYMBIONT, ** TCPWARE_SMTP_SYMBIONT, and SMTP_SERVER images and invoked when needed. ** You can modify this code to change the default behavior. If the ** shareable image MULTINET:USER_SMTP_DISPATCH.EXE (for MultiNet) or ** TCPWARE:USER_SMTP_DISPATCH.EXE (for TCPware) exists, the symbiont and ** server will call these routines in it, instead of the linked-in routines. ** ** THIS INTERFACE IS SUBJECT TO CHANGE WITHOUT NOTICE. ** ** N.B.: While the SMTP symbiont and server processes are currently ** single-threaded, they may be changed to multi-threaded processes ** in a future release. For that reason, the use of static variables ** is strongly discouraged. ** ** Compile and link: ** $ CC USER_SMTP_DISPATCH ** $ LINK/SHARE USER_SMTP_DISPATCH/OPT ** ** Install by placing USER_SMTP_DISPATCH.EXE in the MULTINET: directory ** for MultiNet or the TCPWARE: directory for TCPware and restarting the ** SMTP symbiont with: ** ** $ STOP/QUEUE/NEXT product_SMTP_QUEUE ** $ START/QUEUE product_SMTP_QUEUE. ** ** where "product" is either "MULTINET" or "TCPWARE". ** ** AUTHOR: M. Madison ** COPYRIGHT © 1993, 1994, 1995, 1996 TGV, INC. ** COPYRIGHT © 1997, CISCO SYSTEMS, INC. ** COPYRIGHT © 1999, PROCESS SOFTWARE. ** ALL RIGHTS RESERVED. ** ** CREATION DATE: 05-MAY-1993 ** ** MODIFICATION HISTORY: ** ** 05-MAY-1993 V1.0 Madison Initial coding. ** 05-OCT-1993 V1.0-1 Madison Fix recursive expansion with -1 ctx. ** 02-NOV-1993 V1.0-2 Madison Fix DECNET-DOMAIN stuff. ** 02-DEC-1994 V1.0-3 Madison Fix multi-file reload check logic. ** 14-JUN-1995 V1.0-4 Madison Fix hash of 8-bit characters. ** 29-JUL-1995 V1.1 Madison Add mailhub support. ** 15-FEB-1996 V1.1-1 Madison Fix for expansions hitting local ** forwarder entries. ** 29-APR-1997 V1.2 Madison Gateways now allow port number. ** 27-MAY-1997 V1.2-1 Madison Fix bug in mailing list support. ** 10-SEP-1997 V1.2-2 Madison Fix DECnet domain check. **-- */ /* Modification history ** ** V4.2- 08-Oct-1998 B. Altman D/E 1364 ** add support for gracefull failure on forwarding ** address with illegal format ** ** V4.2-13 09-Oct-1998 B. Altman D/E 854 ** allow aliases of the form of an internet address - user@sys.dom.com ** ** V4.2-14 20-Nov-1998 J. Schreiber ** Changed MULTINET_ALLOW_VIRTUAL_DOMAIN to ** MULTINET_SMTP_ALLOW_VIRTUAL_DOMAIN ** V4.3-15 July 1999 Hunter Goatley ** Modifications to work with both TCPware and MultiNet. ** ** V4.3-17 10-SEP-1999 Hunter Goatley ** Fix trashing of address if a local forwarder is defined. Code ** was checking tmp instead of name.... ** */ #include #include #include #include #if defined(__GNUC__) || defined(TGVBUILD) #include #include #include #include #include #include #include extern char *strchr(); #else /* GNU C or TGV build */ #include #include #include #include #include #include #include #include #include #endif /* GNU C or TGV build */ /* ** For queue handling, use builtin INSQUE and REMQUE instructions, if available. ** Otherwise, use our own routines. */ #ifdef __GNUC__ static void queue_insert(); static int queue_remove(); #else /* __GNUC__ */ #ifdef __DECC #include #ifdef __ALPHA #define queue_insert(item,pred) __PAL_INSQUEL((void *)(pred),(void *)(item)) #define queue_remove(entry,addr) (((struct QUEUE *)entry)->head == entry ? 0 :\ (__PAL_REMQUEL((void *)(entry),(void *)(addr)),1)) #else #define queue_insert(item,pred) _INSQUE(item,pred) #define queue_remove(entry,addr) (((struct QUEUE *)entry)->head == entry ? 0 :\ (_REMQUE(entry,addr),1)) #endif /* __ALPHA */ #else /* __DECC */ #pragma builtins #define queue_insert(item,pred) _INSQUE(item,pred) #define queue_remove(entry,addr) (((struct QUEUE *)entry)->head == entry ? 0 :\ (_REMQUE(entry,addr),1)) #endif /* __DECC */ #endif /* __GNUC__ */ /* ** Structures and macros used in these routines */ struct vmstime {int long0, long1;}; typedef struct vmstime TIME; #define TIMEQL(t1,t2) (((t1).long0 == (t2).long0)&&((t1).long1 == (t2).long1)) #define OK(x) $VMS_STATUS_SUCCESS(x) #define min(a,b) ((a) < (b) ? (a) : (b)) static TIME one_minute={-600000000, -1}; static unsigned int vm_zone = 0; struct QUEUE {void *head, *tail;}; struct ITMLST { unsigned short bufsiz, itmcod; void *bufadr, *retlen; }; typedef enum { HUB_K_LOCAL, HUB_K_REMOTE } HUBTYPE; #define HASH_SIZE 512 /* ** Hash table structure used by the alias_lookup routine */ struct EXPAN { struct EXPAN *flink, *blink; int explen; char expansion[1]; }; struct ALIAS { struct ALIAS *flink, *blink; struct QUEUE expansion_list; int expansion_in_progress; int mailing_list; int owner_len; char *list_owner; char alias[81]; }; static struct QUEUE AliasTable[HASH_SIZE]; /* ** Context structure used by the alias_lookup routine */ struct ACTX { struct ACTX *flink; struct ALIAS *alias; struct EXPAN *expansion; int expanding_file; char *bufp; struct FAB fab; struct RAB rab; }; /* ** Structure used by memory allocation routines */ struct MBUF { unsigned int real_size; unsigned int size; }; /* ** Forward declarations ** ** Entry points */ unsigned int find_gateway_v33(char *, void (*)(), char *, int); unsigned int find_gateway_v41(char *, void (*)(), char *, int, int *); unsigned int alias_lookup_v33(char *, struct ACTX **, void (*)(), void (*)(), char *, int, int *); unsigned int vrfy_lookup_v32(char *, char *, char *, int, void (*)()); unsigned int expn_lookup_v32(char *, struct ACTX **, char *, char *, int, void (*)(), void (*)()); /* ** Support routines */ static unsigned int Alias_Lookup(char *, struct ACTX **, void (*)(), void (*)(), char *, int, int *); static void Fill_Alias_Table(void (*)(), void (*)()); static unsigned int open_file(char *, struct FAB *, struct RAB *); static unsigned int close_file(struct FAB *, struct RAB *); static unsigned int read_file(struct RAB *); static int get_token(char **, char *, int, int *); static void *mem_alloc(int); static void mem_free(void *); static int get_logical(char *, char *, int, int); static int streql_case_blind(char *, char *); static int hash(char *); static unsigned get_file_rdt(char *, TIME *); static int get_forwarder(HUBTYPE, char *, int); static int is_local_address(char *); /* **++ ** ROUTINE: find_gateway_v41 ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is called before delivering mail to a remote host. ** The name of the host is passed in. The routine can return the name ** of a gateway to which the mail should be directed. This can be used ** to implement pseudo-domains like ".BITNET". ** ** This substitution takes place at a high level, before MX expansion ** and processing of the SMTP forwarder setting. A hostname returned by ** this routine is subject to such processing. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** find_gateway_v41(char *host, void (*Dprintf)(), char *gwy, int gwysize, int *port) ** ** host: ASCIZ_string, read only, by reference ** The hostname to be looked up. ** ** Dprintf: procedure, read only, by reference ** A printf-like routine for sending OPCOM messages. ** ** gwy: ASCIZ_string, write only, by reference ** Buffer for result (which should be a null-terminated string). ** ** gwysize: longword, read only, by value ** Size of gwy buffer. ** ** port: Port number to use on connect. ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** SS$_NORMAL: Gateway located ** non-success value: No gateway located ** ** SIDE EFFECTS: None. ** **-- */ unsigned int find_gateway_v33(char *host, void (*Dprintf)(), char *gwy, int gwysize) { return find_gateway_v41(host, Dprintf, gwy, gwysize, 0); } unsigned int find_gateway_v41(char *host, void (*Dprintf)(), char *gwy, int gwysize, int *port) { char tmp[256], *cp, *cp1; int i, have_gateways, have_localdomains; /* ** Initialize to no alternate SMTP port */ if (port != 0) *port = 0; /* ** Gateway processing doesn't apply to numerical addresses ** (of form [a.b.c.d]), although mailhub processing does. */ if (*host == '[') { if (get_forwarder(HUB_K_REMOTE, gwy, gwysize)) return SS$_NORMAL; return 0; } /* ** Shortcut for when the gateways logical isn't there */ have_gateways = get_logical("SMTP_GATEWAYS", tmp, sizeof(tmp), 0); have_localdomains = get_logical("SMTP_LOCALDOMAINS", tmp, sizeof(tmp), 0); if (!(have_gateways || have_localdomains)) { if (get_forwarder(HUB_K_REMOTE, gwy, gwysize)) return SS$_NORMAL; return 0; } /* ** For each domain token, scan the LocalDomains search list. Translation ** strings are of the form ** ** "host.name" or ** ".domain.name" ** ** These are hosts and/or domains to which we can make direct SMTP connections, ** bypassing the mailhub. */ if (have_localdomains) { cp = host; while (*cp != '\0') { for (i = 0; get_logical("SMTP_LOCALDOMAINS", tmp, sizeof(tmp), i); i++) { if (streql_case_blind(cp, tmp)) return 0; } /* ** Increment to next dot (for start of domain name) */ if (*cp == '.') cp++; while (*cp != '\0' && *cp != '.') cp++; } } /* if (have_localdomains) */ /* ** For each domain token, scan the Gateways search list. Translation ** strings are of the form ** ** "name=gateway" ** ** e.g., ".bitnet=cunyvm.cuny.edu" */ if (have_gateways) { cp = host; while (*cp != '\0') { for (i = 0; get_logical("SMTP_GATEWAYS", tmp, sizeof(tmp), i); i++) { for (cp1 = tmp; *cp1 && *cp1 != '='; cp1++); if (*cp1 == '\0') continue; *cp1++ = '\0'; if (streql_case_blind(cp, tmp)) { char *cp2; cp2 = strchr(cp1, '#'); if (cp2 != 0) { *cp2++ = '\0'; if (port != 0) *port = atoi(cp2); } if (strlen(cp1) < gwysize) { strcpy(gwy, cp1); return SS$_NORMAL; } else { (*Dprintf)("SMTP: find_gateway: gateway name %s too long\n", cp1); return 0; /* Gateway found, but too big! */ } } } /* ** Increment to next dot (for start of domain name) */ if (*cp == '.') cp++; while (*cp != '\0' && *cp != '.') cp++; } } /* if (have_gateways) */ /* ** No match. Check to see if we're supposed to treat SMTP-FORWARDER as ** a "mailhub". */ return get_forwarder(HUB_K_REMOTE, gwy, gwysize); } /* find_gateway_v33 */ /* **++ ** ROUTINE: alias_lookup_v33 ** ** FUNCTIONAL DESCRIPTION: ** ** Expands an alias into one or more addresses. Addresses are ** maintained in the alias file(s) defined by the logical name ** MULTINET_ALIAS_FILE. Expansions are returned one at a time; when ** no further expansions are to be provided, a non-success status code ** is returned. ** ** The "ctx" (context) argument is initialized to zero by the caller ** and is reserved for use by this routine. To maintain any context between ** calls, allocate a structure from dynamic memory. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** alias_lookup_v33(char *mailbox, struct ACTX **ctx, void (*Dprintf)(), ** void (*Remove_Local_Hostname)(), char *result, ** int result_size, int *is_list); ** ** mailbox: ASCIZ_string, read only, by reference ** Local mailbox name to be looked up. ** ** ctx: user_arg, modify, by reference ** Context variable for use by this routine. ** ** Dprintf: procedure, read only, by reference ** printf-like routine for sending OPCOM messages. ** ** Remove_Local_Hostname: procedure, read only, by reference ** Procedure that can be called to strip local hostname ** references off of addresses. Takes one parameter, an ** ASCIZ string, which gets modified by the call. ** ** result: ASCIZ_string, write only, by reference ** Buffer for result (which should be a null-terminated ** string). ** ** result_size: longword, read only, by value ** Size of result buffer. ** ** is_list: longword, write only, by reference ** Flag that indicates when an alias refers to a mailing ** list. When set, the result buffer holds the address ** of the list owner, rather than a member of the list. ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** SS$_NORMAL or other success: alias found; result is valid ** non-success: no (more) results ** ** SIDE EFFECTS: None. ** **-- */ unsigned int alias_lookup_v33(char *mailbox, struct ACTX **xctx, void (*Dprintf)(), void (*Remove_Local_Hostname)(), char *result, int result_size, int *is_list) { struct ACTX *ctx; /* ** Check to see if the alias table needs reloading only if we're not ** in the middle of an expansion. */ if (*xctx == 0) { Fill_Alias_Table(Remove_Local_Hostname, Dprintf); } return Alias_Lookup(mailbox, xctx, Dprintf, Remove_Local_Hostname, result, result_size, is_list); } /* alias_lookup_v33 */ /* **++ ** ROUTINE: vrfy_lookup_v32 ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is called when the SMTP server is presented with ** a VRFY command that specifies a local username. By default, if the ** username is valid, the owner field for that username (extracted from ** the UAF) is returned. Otherwise, it will return a non-success status ** value. ** ** You may customize this routine to return any information you ** wish about local users. If you do not want information about local ** users to be divulged (you can base this decision on the inquiring ** host's address, if desired), simply return a non-success status value. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** vrfy_lookup_v32(char *uname, char *remhost, char *text, int text_size, ** void (*Dprintf)()) ** ** uname: ASCIZ_string, read only, by reference ** The username to be looked up. ** ** remhost: ASCIZ_string, read only, by reference ** A string containing the inquiring host's IP address in ** dotted-decimal format. ** ** text: ASCIZ_string, write only, by reference ** Buffer in which to store the result ** (which should be a null-terminated string). ** ** text_size: longword_signed, read only, by value ** Size of the text buffer. ** ** Dprintf: procedure, read only, by reference ** printf-type routine for sending OPCOM messages. ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** SS$_NORMAL: Normal successful completion; text returned ** any non-success status: No text returned ** ** SIDE EFFECTS: None. ** **-- */ unsigned int vrfy_lookup_v32(char *uname, char *remhost, char *text, int text_size, void (*Dprintf)()) { struct ITMLST uailst[2]; struct dsc$descriptor userdsc; char owner[32], user[32], *cp, *cp1; unsigned int status; int i; /* ** Check to see if the username is too long */ userdsc.dsc$w_length = strlen(uname); if (userdsc.dsc$w_length > sizeof(user)) return 0; userdsc.dsc$b_dtype = DSC$K_DTYPE_T; userdsc.dsc$b_class = DSC$K_CLASS_S; userdsc.dsc$a_pointer = user; for (cp = uname, cp1 = user; *cp; cp++, cp1++) *cp1 = _toupper(*cp); uailst[0].bufsiz = sizeof(owner); uailst[0].itmcod = UAI$_OWNER; uailst[0].bufadr = owner; uailst[0].retlen = 0; uailst[1].bufsiz = uailst[1].itmcod = 0; status = sys$getuai(0,0,&userdsc,uailst,0,0,0); if ($VMS_STATUS_SUCCESS(status)) { i = (unsigned char) owner[0]; if (i >= text_size) i = text_size - 1; strncpy(text, owner + 1, i); text[i] = '\0'; } return status; } /* vrfy_lookup_v32 */ /* **++ ** ROUTINE: expn_lookup_v32 ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is called when the SMTP server is presented with ** an EXPN command for expanding a local alias. By default, it uses ** the local alias expansion routines in this module to perform the ** expansion. ** ** You may modify this routine to return any information you wish ** about local aliases or mailing lists. If you do not wish to have ** such information divulged, simply have the routine return a non-success ** status on the first call. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** expn_lookup_v32(char *mailbox, struct ACTX **ctx, char *remhost, ** char *result, int result_size, ** void (*Dprintf)(), void (*Remove_Local_Hostname)()) ** ** mailbox: ASCIZ_string, read only, by reference ** Local mailbox name to be expanded. ** ** ctx: user_arg, modify, by reference ** Context variable for use by this routine. ** ** remhost: ASCIZ_string, read only, by reference ** Inquiring host's IP address, in dotted-decimal format. ** ** result: ASCIZ_string, write only, by reference ** Buffer for returning the resulting expansion ** (which should be a null-terminated string). ** ** result_size: longword_unsigned, read only, by value ** Size of result buffer. ** ** Dprintf: procedure, read only, by reference ** printf-like routine for sending OPCOM messages. ** ** Remove_Local_Hostname: procedure, read only, by reference ** Routine to remove local hostname references from addresses. ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: See alias_lookup_v33. ** ** SIDE EFFECTS: None. ** **-- */ unsigned int expn_lookup_v32(char *mailbox, struct ACTX **ctx, char *remhost, char *result, int result_size, void (*Dprintf)(), void (*Remove_Local_Hostname)()) { int is_list; unsigned int status; static char owner_flag[] = " (list owner)"; status = alias_lookup_v33(mailbox, ctx, Dprintf, Remove_Local_Hostname, result, result_size, &is_list); if ($VMS_STATUS_SUCCESS(status) && is_list) { if (strlen(result) < result_size-sizeof(owner_flag)) { strcat(result, owner_flag); } } return status; } /* expn_lookup_v32 */ /* **++ ** ROUTINE: Alias_Lookup ** ** FUNCTIONAL DESCRIPTION: ** ** Recursive alias table search routine. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** Alias_Lookup(char *name, struct ACTX **ctx, void (*Dprintf)(), ** void (*Remove_Local_Hostname)(), ** char *result, int result_size, int *is_list) ** ** name: ASCIZ_string, read only, by reference ** ctx: varying_arg, modify, by reference ** Dprintf: procedure, read only, by reference ** Remove_Local_Hostname: procedure, read only, by reference ** result: ASCIZ_string, write only, by reference ** result_size: longword, read only, by value ** is_list: longword (boolean), write only, by reference ** ** IMPLICIT INPUTS: AliasTable. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** success: alias found, result is valid ** non-success: no result ** ** SIDE EFFECTS: None. ** **-- */ static unsigned int Alias_Lookup(char *name, struct ACTX **xctx, void (*Dprintf)(), void (*Remove_Local_Hostname)(), char *result, int result_size, int *is_list) { struct ACTX *ctx, *myctx, *a; struct ALIAS *p; struct EXPAN *exp; char tmp[256], *cp; unsigned int status; int len, i, j; char retbuf[255]; *is_list = 0; /* ** Clean up after "fake" context returned by DECnet check code below. */ if (*xctx == (struct ACTX *) -1) return 0; /* ** If it's not a local address (i.e., contains an @-sign), ** check to see if it's a DECnet domain address; if it's not, ** don't bother expanding it. */ if (is_local_address(name) != 1) { /* ** Check to see if the address is part of the "DECnet" mail domain - form: ** ** user@node[.node...].decnet-domain ** (e.g., madison@node2.node1.dnet.tgv.com) ** ** If it is of that form, convert it back into a DECnet-style local address ** ** (e.g., "node1::node2::madison") */ if (get_logical("SMTP_DECNET_DOMAIN", tmp, sizeof(tmp), 0)) { register char *cp1, *cp2, *cp3; cp = strchr(name, '@'); if (cp == 0) return 0; cp1 = cp + strlen(cp) - strlen(tmp); if (streql_case_blind(cp1, tmp)) { cp2 = result; *cp2++ = '"'; len = 1; while (--cp1 > cp) { for (cp3 = cp1; cp3 > cp+1 && *(cp3-1) != '.'; cp3--); if (len + (cp1-cp3) > result_size-4) return 0; strncpy(cp2, cp3, cp1-cp3); cp2 += (cp1-cp3); *cp2++ = ':'; *cp2++ = ':'; len += (cp1-cp3) + 2; cp1 = cp3; } if (len + (cp-name) > result_size-2) return 0; strncpy(cp2, name, cp-name); cp2 += (cp-name); *cp2++ = '"'; *cp2 = '\0'; *xctx = (struct ACTX *) -1; return SS$_NORMAL; } } if(!get_logical("SMTP_ALLOW_VIRTUAL_DOMAIN",retbuf, 255,0)) return 0; /* ba1 */ } /* ** It's a "local" address (i.e., no domain), or we are allowing virtual domains. If it begins ** with an underscore, don't expand. */ if (*name == '_') return 0; /* ** The initial lookup */ if (*xctx == 0) { ctx = 0; i = hash(name); for (p = AliasTable[i].head; p != (struct ALIAS *) &AliasTable[i]; p = p->flink) { if (streql_case_blind(name, p->alias)) { if (p->expansion_in_progress) break; p->expansion_in_progress = 1; ctx = mem_alloc(sizeof(struct ACTX)); if (ctx == 0) { (*Dprintf)("SMTP: alias_lookup: memory allocation failure\n"); return 0; } ctx->alias = p; *xctx = ctx; /* ** If it's a mailing list, we set the "is_list" flag and return the ** list owner first (if there is a list owner). We do this only if ** we can successfully open the file holding the mailing list. */ if (p->mailing_list) { exp = p->expansion_list.head; if (p->mailing_list == 1) status = open_file(exp->expansion, &ctx->fab, &ctx->rab); else status = 1; /* mailing list w/o external file */ if (OK(status)) { len = min(result_size-1, p->owner_len); if (len > 0) strncpy(result, p->list_owner, len); result[len] = '\0'; *is_list = 1; return SS$_NORMAL; } else { (*Dprintf)("SMTP: alias_lookup: could not open mailing list file %s for alias %s\n", exp->expansion, p->alias); p->expansion_in_progress = 0; mem_free(ctx); *xctx = 0; return 0; } } break; } } /* ** If we get to this point, it's either because we found a non-mailing-list ** alias, or because we didn't find a thing (in which case ctx will be 0). */ if (ctx == 0) { /* ** If a local user, see if we should tack on "@mailhub" */ if (is_local_address(name) == 1) { char tmp1[256]; if (get_forwarder(HUB_K_LOCAL, tmp1, sizeof(tmp1))) { len = strlen(name); strcpy(result, name); result[len++] = '@'; strcpy(result+len, tmp1); *xctx = (struct ACTX *) -1; return SS$_NORMAL; } return 0; } return 0; } /* if (ctx == 0) -- no expansion */ /* ** Return the first address in the expansion list. If there are none, ** display a message and treat as non-alias. */ ctx->expansion = p->expansion_list.head; if (ctx->expansion == (struct EXPAN *) &ctx->alias->expansion_list) { /*********************** If you would like this case reported as an error, uncomment these lines. (*Dprintf)("SMTP: alias_lookup: alias %s has no expansion list\n", p->alias); ************************/ ctx->alias->expansion_in_progress = 0; mem_free(ctx); return 0; } /* ** If the expansion is not "*" (which means to use the original name without ** recursive expansion), we try recursive expansion. */ if (!((ctx->expansion->explen == 1 && *ctx->expansion->expansion == '*') || (ctx->expansion->explen > 2 && *ctx->expansion->expansion == '"' && ctx->expansion->expansion[1] == '<'))) { myctx = 0; status = Alias_Lookup(ctx->expansion->expansion, &myctx, Dprintf, Remove_Local_Hostname, result, result_size, &i); if (OK(status) && i) status = Alias_Lookup(ctx->expansion->expansion, &myctx, Dprintf, Remove_Local_Hostname, result, result_size, &i); if (OK(status)) { if (myctx == (struct ACTX *) -1) { *xctx = myctx; } else { for (a = myctx; a->flink != 0; a = a->flink); a->flink = ctx; *xctx = myctx; } return SS$_NORMAL; } } /* ** If the expansion is "*", then we use the original name as the expansion. ** If the expansion is of the form "expansion->explen == 1 && *ctx->expansion->expansion == '*') { len = min(result_size-1, strlen(name)); strncpy(result, name, len); } else if (ctx->expansion->explen > 3 && ctx->expansion->expansion[0] == '"' && ctx->expansion->expansion[ctx->expansion->explen-1] == '"' && ctx->expansion->expansion[1] == '<') { char fspec[256]; int len; len = ctx->expansion->explen - 3; if (len > sizeof(fspec)-1) len = sizeof(fspec)-1; strncpy(fspec, ctx->expansion->expansion+2, len); fspec[len] = '\0'; status = open_file(fspec, &ctx->fab, &ctx->rab); if (OK(status)) { ctx->bufp = 0; ctx->expanding_file = 1; } *xctx = ctx; *is_list = 0; goto continue_expansion; /* even if we couldn't open the file */ } else { len = min(result_size-1, ctx->expansion->explen); strncpy(result, ctx->expansion->expansion, len); } result[len] = '\0'; *xctx = ctx; *is_list = 0; return SS$_NORMAL; } /* if (*xctx == 0) -- initial expansion */ /* ** We get here when an expansion is in progress (non-null ctx). */ ctx = *xctx; continue_expansion: while (ctx != 0) { /* ** Read the next mailing list entry, or fetch the next expansion from ** the expansion list. */ if (ctx->alias->mailing_list == 1 || ctx->expanding_file) { while (1) { if (ctx->bufp == 0) { status = read_file(&ctx->rab); if (!OK(status)) { close_file(&ctx->fab, &ctx->rab); len = 0; if (ctx->expanding_file) { ctx->expanding_file = 0; goto continue_expansion; } else break; } ctx->bufp = ctx->rab.rab$l_ubf; } j = get_token(&ctx->bufp, tmp, sizeof(tmp), &len); if (j == 0) { (*Remove_Local_Hostname)(tmp); if (is_local_address(tmp) == 2) goto continue_expansion; /* don't allow ">file-spec" in external files! */ len = strlen(tmp); break; } if (j > 0) ctx->bufp = 0; else ctx->bufp++; } if (len > 0) status = SS$_NORMAL; else status = 0; } else { if (ctx->alias->mailing_list == 2 && ctx->expansion == 0) { ctx->expansion = ctx->alias->expansion_list.head; } else { ctx->expansion = ctx->expansion->flink; } if (ctx->expansion == (struct EXPAN *)&ctx->alias->expansion_list) { status = 0; } else { len = min(sizeof(tmp)-1, ctx->expansion->explen); strncpy(tmp, ctx->expansion->expansion, len); tmp[len] = '\0'; status = SS$_NORMAL; } } if (OK(status)) break; /* ** If there are no more expansions for this alias, pop the current context ** off the stack and continue at the next level, if there is one. */ ctx->alias->expansion_in_progress = 0; *xctx = ctx->flink; mem_free(ctx); ctx = *xctx; } /* ** End of the line? */ if (ctx == 0) return 0; /* ** We have an expansion of some kind. If it does not contain an @ sign, ** it's a local mailbox which might actually be another alias, so we ** look it up. ** ** An expansion of "*" says to use the name without recursing. An expansion ** in quotation marks beginning with "<" means we should continue reading from ** a file. */ if (!ctx->expanding_file && (len > 3 && tmp[0] == '"' && tmp[1] == '<' && tmp[len-1] == '"')) { tmp[len-1] = '\0'; status = open_file(tmp+2, &ctx->fab, &ctx->rab); if (OK(status)) { ctx->bufp = 0; ctx->expanding_file = 1; } goto continue_expansion; /* even if we couldn't open the file */ } if (!(tmp[0] == '*' && tmp[1] == '\0') && (strchr(tmp, '@') == 0)) { myctx = 0; status = Alias_Lookup(tmp, &myctx, Dprintf, Remove_Local_Hostname, result, result_size, &i); /* ** If it's a mailing list, we don't care about the list owner, so just ** move on to the first entry. */ if (OK(status) && i) status = Alias_Lookup(tmp, &myctx, Dprintf, Remove_Local_Hostname, result, result_size, &i); /* ** It's an alias all right. Push the current context on the stack and ** return the first expansion from the recursive call. */ if (OK(status)) { /* ** If the returned context was -1, there was an expansion that we need ** not follow any further (typically a DECnet address or forwarding entry). ** Otherwise, push the new context on our stack. */ if (myctx != (struct ACTX *) -1) { for (a = myctx; a->flink != 0; a = a->flink); a->flink = ctx; *xctx = myctx; } return SS$_NORMAL; } } /* ** Not an alias, just an address. Return it to the caller. ** ** If tmp is "*", then we return the original name to the ** caller without recursive expansion on it. */ if (tmp[0] == '*' && tmp[1] == '\0') strcpy(tmp, name); len = min(len, result_size-1); strncpy(result, tmp, len); result[len] = '\0'; return SS$_NORMAL; } /* Alias_Lookup */ /* **++ ** ROUTINE: Fill_Alias_Table ** ** FUNCTIONAL DESCRIPTION: ** ** Loads the alias table. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** Fill_Alias_Table(void (*Remove_Local_Hostname)(), void (*Dprintf)()) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** ** ** SIDE EFFECTS: AliasTable modified. ** **-- */ static void Fill_Alias_Table(void (*Remove_Local_Hostname)(), void (*Dprintf)()) { #define AFILE_MAX 16 static int Initialized = 0, file_count = 0; static TIME HoldTime; static struct AFILE { TIME LoadTime; char FileName[256]; } file_list[AFILE_MAX]; char tmp[1024], *cp, *cp1; struct EXPAN *exp; TIME now, xtime, ytime; int i, j, len, reload, reload_this; unsigned int status; sys$gettim(&now); if (Initialized) { lib$add_times(&HoldTime, &one_minute, &xtime); if (OK(lib$sub_times(&xtime, &now, &ytime))) return; } HoldTime = now; reload = 0; for (i = 0; i < AFILE_MAX; i++) { reload_this = 0; if (!get_logical("ALIAS_FILE", tmp, sizeof(tmp), i)) { if (i == 0) { if (get_logical("KERNEL_BASE_ADDRESS", tmp, sizeof(tmp), 0)) { strcpy(tmp, "MULTINET:SMTP_ALIASES."); } else { strcpy(tmp, "TCPWARE:SMTP_ALIASES."); } } else break; } if (i >= file_count) { reload_this = 1; strcpy(file_list[i].FileName, tmp); get_file_rdt(file_list[i].FileName, &file_list[i].LoadTime); file_count++; } else if (streql_case_blind(file_list[i].FileName, tmp)) { reload_this = !OK(get_file_rdt(file_list[i].FileName, &xtime)); if (!reload_this) reload_this = !TIMEQL(file_list[i].LoadTime, xtime); if (reload_this) file_list[i].LoadTime = xtime; } else { reload_this = 1; strcpy(file_list[i].FileName, tmp); get_file_rdt(file_list[i].FileName, &file_list[i].LoadTime); } if (!reload) reload = reload_this; } if (!reload) return; if (!Initialized) { Initialized = 1; for (i = 0; i < HASH_SIZE; i++) AliasTable[i].head = AliasTable[i].tail = &AliasTable[i]; } for (i = 0; i < HASH_SIZE; i++) { struct ALIAS *p; struct EXPAN *e; while (queue_remove(AliasTable[i].head, &p)) { while (queue_remove(p->expansion_list.head, &e)) mem_free(e); if (p->list_owner) mem_free(p->list_owner); mem_free(p); } } for (i = 0; i < file_count; i++) { struct FAB fab; struct RAB rab; struct ALIAS *alias; int line_count; status = open_file(file_list[i].FileName, &fab, &rab); if (!OK(status)) continue; line_count = 0; while (OK(read_file(&rab))) { line_count++; for (cp = rab.rab$l_ubf; isspace(*cp); cp++); if (*cp == '\0' || *cp == '#') continue; alias = mem_alloc(sizeof(struct ALIAS)); if (alias == 0) { (*Dprintf)("SMTP: alias_load: memory allocation failure\n"); break; } alias->expansion_list.head = alias->expansion_list.tail = &alias->expansion_list; for (cp1 = alias->alias, j = 80; j > 0 && *cp && *cp != ':'; j--, cp++) if (!isspace(*cp)) *cp1++ = _tolower(*cp); *cp1 = '\0'; if (*cp != ':') { (*Dprintf)("SMTP: alias file %s has bad format, line %d\n", file_list[i].FileName, line_count); mem_free(alias); continue; } cp++; /* ** For a mailing list, format is ** ** name :: owner-address, filename; ** ** where the owner-address will get used as the error return path, and ** the list members' addresses are stored in the file. */ if (*cp == ':') { alias->mailing_list = 1; cp++; } else { /* ** Allow mailing lists with an "owner" address, without having to ** rely on an external file. */ char *ab = strchr(alias->alias, '<'); if (ab != 0 && alias->alias[strlen(alias->alias)-1] == '>') { alias->mailing_list = 2; alias->owner_len = strlen(ab) - 2; alias->list_owner = mem_alloc(alias->owner_len+1); if (alias->list_owner == 0) { (*Dprintf)("alias_load: memory allocation failure\n"); } else { if (alias->owner_len > 0) strncpy(alias->list_owner, ab+1, alias->owner_len); alias->list_owner[alias->owner_len] = '\0'; } while (ab > &(alias->alias[0]) && isspace(*(ab-1))) ab--; *ab = '\0'; } } while (1) { j = get_token(&cp, tmp, sizeof(tmp), &len); if (j > 0) { /* at end of line, get next one */ while (1) { if (!OK(read_file(&rab))) { j = -1; break; } line_count++; for (cp = rab.rab$l_ubf; isspace(*cp); cp++); if (!(*cp == '\0' || *cp == '#')) break; } } if (j < 0) break; /* hit the semicolon or end-of-file */ if (j == 0) { if (alias->mailing_list == 1) { if (alias->list_owner == 0) { (*Remove_Local_Hostname)(tmp); alias->owner_len = strlen(tmp); alias->list_owner = mem_alloc(alias->owner_len+1); if (alias->list_owner == 0) { (*Dprintf)("alias_load: memory allocation failure\n"); break; } strcpy(alias->list_owner, tmp); continue; } } else { (*Remove_Local_Hostname)(tmp); len = strlen(tmp); } exp = mem_alloc(sizeof(struct EXPAN) + len); if (exp == 0) { (*Dprintf)("alias_load: memory allocation failure\n"); break; } exp->explen = len; strcpy(exp->expansion, tmp); queue_insert(exp, alias->expansion_list.tail); } } queue_insert(alias, AliasTable[hash(alias->alias)].tail); } close_file(&fab, &rab); } return; } /* Fill_Alias_Table */ /* **++ ** ROUTINE: open_file ** ** FUNCTIONAL DESCRIPTION: ** ** Opens a file using RMS. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** open_file(char *fspec, struct FAB *fab, struct RAB *rab) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: any from RMS routines. ** ** SIDE EFFECTS: None. ** **-- */ static unsigned int open_file(char *fspec, struct FAB *fab, struct RAB *rab) { struct XABFHC xabfhc; unsigned int status; *fab = cc$rms_fab; *rab = cc$rms_rab; xabfhc = cc$rms_xabfhc; fab->fab$l_fna = fspec; fab->fab$b_fns = strlen(fspec); fab->fab$l_xab = (void *) &xabfhc; fab->fab$b_fac = FAB$M_GET; status = sys$open(fab, 0, 0); if (!OK(status)) return status; rab->rab$l_fab = fab; status = sys$connect(rab, 0, 0); if (!OK(status)) { sys$close(fab, 0, 0); return status; } rab->rab$w_usz = (fab->fab$w_mrs == 0) ? ((xabfhc.xab$w_lrl == 0) ? 32768 : xabfhc.xab$w_lrl+1) : (fab->fab$w_mrs + 1); rab->rab$l_ubf = mem_alloc(rab->rab$w_usz); if (rab->rab$l_ubf == 0) { sys$disconnect(rab,0,0); sys$close(fab,0,0); return SS$_INSFMEM; } return SS$_NORMAL; } /* open_file */ /* **++ ** ROUTINE: close_file ** ** FUNCTIONAL DESCRIPTION: ** ** Closes a file. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** close_file(struct FAB *fab, struct RAB *rab); ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: any from RMS routines. ** ** SIDE EFFECTS: None. ** **-- */ static unsigned int close_file(struct FAB *fab, struct RAB *rab) { if (rab->rab$l_ubf) mem_free(rab->rab$l_ubf); sys$disconnect(rab, 0, 0); sys$close(fab, 0, 0); return SS$_NORMAL; } /* close_file */ /* **++ ** ROUTINE: read_file ** ** FUNCTIONAL DESCRIPTION: ** ** Reads a record from a file. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** read_file(struct FAB *fab, struct RAB *rab); ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: any from RMS routines. ** ** SIDE EFFECTS: None. ** **-- */ static unsigned int read_file(struct RAB *rab) { unsigned int status; status = sys$get(rab, 0, 0); if (OK(status)) rab->rab$l_ubf[rab->rab$w_rsz] = '\0'; return status; } /* read_file */ /* **++ ** ROUTINE: get_token ** ** FUNCTIONAL DESCRIPTION: ** ** Fetches a token from a line. A comma after token indicates ** that there is another token in the list. A semicolon after a token ** indicates the end of a list. The pound sign is used to begin a comment. ** ** RETURNS: int ** ** PROTOTYPE: ** ** get_token(char **bufp, char *result, int result_size, int *len) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** < 0 : hit a semicolon ** = 0 : result is valid ** > 0 : hit end of buffer ** ** ** SIDE EFFECTS: None. ** **-- */ static int get_token(char **bufp, char *result, int result_size, int *len) { char *cp, *cp1; int inq; cp = *bufp; cp1 = result; while (*cp && (isspace(*cp) || *cp == ',')) cp++; if (*cp == '\0' || *cp == '#') { *bufp = cp; return 1; } if (*cp == ';') { *bufp = cp; return -1; } inq = 0; #define ADD(c) {if (result_size > 1) {*cp1++ = c; result_size--;}} while (1) { if (inq) { if (*cp == '\\') { ADD(*cp++); ADD(*cp); } else if (*cp == '"') { ADD(*cp); inq = 0; } else if (*cp == '\0') break; else ADD(*cp); } else { if (*cp == '\0' || *cp == '#' || *cp == ';' || *cp == ',') break; if (*cp == '"') inq = 1; if (!isspace(*cp)) *cp1++ = *cp; } cp++; } *cp1 = '\0'; *len = cp1-result; *bufp = cp; return 0; } /* get_token */ /* **++ ** ROUTINE: queue_insert ** ** FUNCTIONAL DESCRIPTION: ** ** Inserts an entry into a queue. The QUE structure isn't ** required, as long as the first two longwords of the structure ** being used is a queue header. ** ** USED ONLY WITH GNU C. VAX C and DEC C use the appropriate builtins. ** ** RETURNS: void ** ** PROTOTYPE: ** ** queue_insert(item, pred) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ #ifdef __GNUC__ static void queue_insert(struct QUEUE *item, struct QUEUE *pred) { item->head = pred->head; item->tail = pred; ((struct QUEUE *)(pred->head))->tail = item; pred->head = item; } #endif /* **++ ** ROUTINE: queue_remove ** ** FUNCTIONAL DESCRIPTION: ** ** Removes an entry from a queue, if there is one. ** ** USED ONLY WITH GNU C. VAX C and DEC C use the appropriate builtins. ** ** RETURNS: int ** ** PROTOTYPE: ** ** queue_remove(struct QUEUE *entry, void **addr); ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 1: Remove successful. ** 0: No entry to remove. ** ** SIDE EFFECTS: None. ** **-- */ #ifdef __GNUC__ static int queue_remove(struct QUEUE *entry, void **addr) { if (entry->head == entry) return 0; ((struct QUEUE *)(entry->tail))->head = entry->head; ((struct QUEUE *)(entry->head))->tail = entry->tail; *addr = entry; return 1; } #endif /* **++ ** ROUTINE: mem_alloc ** ** FUNCTIONAL DESCRIPTION: ** ** Reentrant memory allocation routine based on LIB$GET_VM. ** ** RETURNS: void * ** ** PROTOTYPE: ** ** mem_alloc(int size) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 0: error! ** non-0: address of allocated block ** ** SIDE EFFECTS: None. ** **-- */ static void *mem_alloc(int size) { unsigned int status; struct MBUF *m; if (vm_zone == 0) { unsigned int alg = LIB$K_VM_FIRST_FIT; unsigned int flags = LIB$M_VM_BOUNDARY_TAGS|LIB$M_VM_GET_FILL0| LIB$M_VM_FREE_FILL1; static $DESCRIPTOR(zone_name, "USMTPD_ZONE"); status = lib$create_vm_zone(&vm_zone, &alg, 0, &flags, 0, 0, 0, 0, 0, 0, &zone_name); if (!$VMS_STATUS_SUCCESS(status)) return 0; } size += sizeof(struct MBUF); status = lib$get_vm(&size, &m, &vm_zone); if ($VMS_STATUS_SUCCESS(status)) { m->size = size - sizeof(struct MBUF); m->real_size = size; return (void *) (m + 1); } return 0; } /* mem_alloc */ /* **++ ** ROUTINE: mem_free ** ** FUNCTIONAL DESCRIPTION: ** ** Frees memory allocated with mem_alloc. ** ** RETURNS: void ** ** PROTOTYPE: ** ** mem_free(void *ptr) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: None. ** ** SIDE EFFECTS: None. ** **-- */ static void mem_free(void *p) { struct MBUF *m; unsigned int size; m = (struct MBUF *) ((char *) p - sizeof(struct MBUF)); if (m->real_size == m->size + sizeof(struct MBUF)) { size = m->real_size; lib$free_vm(&size, &m, &vm_zone); } return; } /* mem_free */ /* **++ ** ROUTINE: get_logical ** ** FUNCTIONAL DESCRIPTION: ** ** Translates a logical name. Can be used to translate ** search list logicals by passing in a non-zero index value. ** ** RETURNS: boolean ** ** PROTOTYPE: ** ** get_logical(char *lognam, char *eqvnam, int eqvsiz, int indx) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 1: success ** 0: failure ** ** SIDE EFFECTS: None. ** **-- */ static int get_logical(char *lognam, char *eqvnam, int eqvsiz, int indx) { static $DESCRIPTOR(tabnam,"LNM$FILE_DEV"); static unsigned int attr = LNM$M_CASE_BLIND; struct dsc$descriptor lnmdsc; unsigned short len; struct ITMLST lnmlst[3]; unsigned int status; char new_logical[255]; lnmlst[0].bufsiz = sizeof(indx); lnmlst[0].itmcod = LNM$_INDEX; lnmlst[0].bufadr = &indx; lnmlst[0].retlen = 0; lnmlst[1].bufsiz = eqvsiz-1 > 255 ? 255 : eqvsiz-1; lnmlst[1].itmcod = LNM$_STRING; lnmlst[1].bufadr = eqvnam; lnmlst[1].retlen = &len; lnmlst[2].itmcod = lnmlst[2].bufsiz = 0; lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; lnmdsc.dsc$b_class = DSC$K_CLASS_S; strcpy (new_logical, "TCPWARE_"); strcat (new_logical, lognam); lnmdsc.dsc$a_pointer = new_logical; lnmdsc.dsc$w_length = strlen(new_logical); status = sys$trnlnm(&attr,&tabnam,&lnmdsc,0,lnmlst); if (!(status & 1)) { strcpy (new_logical, "MULTINET_"); strcat (new_logical, lognam); lnmdsc.dsc$a_pointer = new_logical; lnmdsc.dsc$w_length = strlen(new_logical); status = sys$trnlnm(&attr,&tabnam,&lnmdsc,0,lnmlst); } if ($VMS_STATUS_SUCCESS(status) && len > 0) { eqvnam[len] = '\0'; return 1; } return 0; } /* get_logical */ /* **++ ** ROUTINE: streql_case_blind ** ** FUNCTIONAL DESCRIPTION: ** ** Case-blind string comparison ** ** RETURNS: boolean ** ** PROTOTYPE: ** ** streql_case_blind(char *str1, char *str2) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 1: equal ** 0: not equal ** ** SIDE EFFECTS: None. ** **-- */ static int streql_case_blind(char *s1, char *s2) { register char c1, c2; while (1) { c1 = _toupper(*s1); c2 = _toupper(*s2); if (c1 != c2) break; if (c1 == '\0') return 1; s1++; s2++; } return 0; } /* streql_case_blind */ /* **++ ** ROUTINE: hash ** ** FUNCTIONAL DESCRIPTION: ** ** tbs ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** tbs ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** ** ** SIDE EFFECTS: None. ** **-- */ static int hash(char *s) { int hashval; register char *cp; hashval = 0; for (cp = s; *cp != '\0'; cp++) hashval += (unsigned char) _tolower(*cp); return hashval % HASH_SIZE; } /* **++ ** ROUTINE: get_file_rdt ** ** FUNCTIONAL DESCRIPTION: ** ** Fetches the RDT for a file. ** ** RETURNS: cond_value, longword (unsigned), write only, by value ** ** PROTOTYPE: ** ** get_file_rdt(fspec, rdt) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** ** ** SIDE EFFECTS: None. ** **-- */ static unsigned int get_file_rdt(char *fspec, TIME *rdt) { struct FAB fab; struct XABRDT xabrdt; unsigned int status; fab = cc$rms_fab; xabrdt = cc$rms_xabrdt; fab.fab$l_fna = fspec; fab.fab$b_fns = strlen(fspec); fab.fab$b_fac = FAB$M_GET; fab.fab$l_xab = (char *) &xabrdt; status = sys$open(&fab,0,0); if ($VMS_STATUS_SUCCESS(status)) { memcpy(rdt, &xabrdt.xab$q_rdt, 8); sys$close(&fab,0,0); } else { rdt->long0 = rdt->long1 = 0; } return status; } /* get_file_rdt */ /* **++ ** ROUTINE: get_forwarder ** ** FUNCTIONAL DESCRIPTION: ** ** Gets information about a "mailhub" mail forwarder. Handles both ** the pre-V3.5 logical names from the "mailhub" add-on package, as well ** as the FORWARD_REMOTE/FORWARD_LOCAL mechanism introduced in V3.5. ** ** RETURNS: int ** ** PROTOTYPE: ** ** get_forwarder(HUBTYPE type, char *gwybuf, int bufsize) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 0: no mailhub ** 1: mailhub present ** ** SIDE EFFECTS: None. ** **-- */ int get_forwarder (HUBTYPE type, char *buf, int bufsize) { char tmp[256]; int i; if (type == HUB_K_REMOTE) { if (get_logical("SMTP_MAILHUB", buf, bufsize, 0)) return 1; if (get_logical("SMTP_FORWARD_REMOTE", tmp, sizeof(tmp), 0)) { if (strchr("TtYy1", tmp[0]) != 0) { return get_logical("SMTP_FORWARDER", buf, bufsize, 0); } } return 0; } if (type == HUB_K_LOCAL) { if (get_logical("SMTP_LOCAL_TO_MAILHUB", tmp, sizeof(tmp), 0)) { if (strchr("YyTt1", tmp[0]) != 0) { return get_logical("SMTP_MAILHUB", buf, bufsize, 0); } else { return 0; } } if (get_logical("SMTP_FORWARD_LOCAL", tmp, sizeof(tmp), 0)) { if (strchr("YyTt1", tmp[0])) { return get_logical("SMTP_FORWARDER", buf, bufsize, 0); } } return 0; } return 0; /* unknown type */ } /* get_forwarder */ /* **++ ** ROUTINE: is_local_address ** ** FUNCTIONAL DESCRIPTION: ** ** Checks to see if an address is for a local user (looks for @host-name) ** ** RETURNS: int ** ** PROTOTYPE: ** ** is_local_address(char *address) ** ** IMPLICIT INPUTS: None. ** ** IMPLICIT OUTPUTS: None. ** ** COMPLETION CODES: ** 1: is local ** 0: is remote ** ** SIDE EFFECTS: None. ** **-- */ static int is_local_address(char *address) { char *cp; int inquotes; inquotes = 0; for (cp = address; *cp != '\0'; cp++) { if (inquotes) { if (*cp == '\\') { cp++; if (*cp == '\0') break; } else if (*cp == '"') { inquotes = 0; } } else { if (*cp == '\\') { cp++; if (*cp == '\0') break; } else if (*cp == '"') { inquotes = 1; } else if (*cp == '@') return 0; } } /* ** Looks local, but if it's ">something" then say it's not so ** we don't do mailhub processing on it. */ if (*address == '"') if (address[1] == '>') return 2; return 1; } /* is_local_address */