/*****************************************************************************/ /* address.c An address list is a string containing one or more email addresses (separated by newline characters). Any newline character should be preceded by a comma (making it a comma-separated list) and therefore directly usable in an RFC2822 header field. COPYRIGHT --------- Copyright (C) 2005-2023 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. VERSION HISTORY --------------- 27-JUN-2010 MGD bugfix; AddressListMassage() comma processing 13-NOV-2007 MGD bugfix; AddressListMassage() [smtp-default-host] detect VMS Mail override using explicit 0:: 30-JUN-2007 MGD bugfix; AddressIsVms() and AddressIsRfc() requires AddressListExtract() to parse an address from a list 20-JUL-2006 MGD AddressListMassage() allow for comma-separated addresses 12-JUL-2006 MGD bugfix; AddressIsVms() allow for empty string 06-JUL-2006 MGD bugfix; AddressListMassage() better VMS address detection 06-MAY-2006 MGD bugfix; AddressIsRfc() RFC2822 only allows the double quotation character (0x34) to quote bugfix; allow backslash (0x92) to escape a single character 14-MAR-2006 MGD AddressListMassage() refine address reduction (yep - again!) also allow for appending a default host/domain if the configuration directive [SMTP-defaulthost] is set 15-FEB-2006 MGD AddressListMassage() refine "
,
" reduction 01-FEB-2005 MGD initial */ /*****************************************************************************/ #ifdef SOYMAIL_VMS_V7 #undef _VMS_V6_SOURCE #define _VMS_V6_SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif #pragma nomember_alignment /* standard C header files */ #include #include #include #include #include #include /* VMS related header files */ #include #include /* application header file */ #include #include #include #include #define FI_LI __FILE__, __LINE__ /* global storage */ /* prototypes */ /* external storage */ extern BOOL Debug, WatchEnabled; extern CONFIG_DATA SoyMailConfig; /*****************************************************************************/ /* This function appends the string supplied as 'AddrPtr' to the list of addresses pointed to by 'ListPtr'using dynamic memory. The memory previously allocated by 'ListPtr' is freed. Returns a pointer to the new list. Expects to be subsequently massaged by AddressListMassage(). */ char* AddressListAppend ( char *ListPtr, char *AddrPtr ) { int len; char *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddressListAppend() |%s|\n", AddrPtr); if (!AddrPtr || !AddrPtr[0]) return (ListPtr); if (ListPtr) len = strlen(ListPtr) + strlen(AddrPtr) + 2; else len = strlen(AddrPtr) + 1; sptr = CgiLibVeeMemCalloc (len); if (ListPtr && ListPtr[0]) sprintf (sptr, "%s\n%s", ListPtr, AddrPtr); else strcpy (sptr, AddrPtr); CgiLibVeeMemFree (ListPtr); if (Debug) fprintf (stdout, "|%s|\n", sptr); return (sptr); } /*****************************************************************************/ /* This function ensures that the string list conforms to the address list requirements described in the prologue to this module. Returns a pointer to a reformatted, compliant string. Returns NULL if there was a significant problem (e.g. buffer overflow). If the parameter string is dynamic it is up to the calling function to dispose of it. */ char* AddressListMassage ( char* ListPtr, BOOL CommaSeparate ) { BOOL CommaNext; int ccnt, lcnt, NewListSize; char *cptr, *lptr, *nlptr, *sptr, *zptr, *NewListPtr; char abuf [256]; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MASSAGE from |!AZ", ListPtr ? ListPtr : "(null)"); if (!(lptr = ListPtr)) return (NULL); NewListSize = 0; /* allocate an empty list (just in case) */ NewListPtr = nlptr = CgiLibVeeMemCalloc (8); if (!NewListPtr) ErrorExit (vaxc$errno, FI_LI); lptr = ListPtr; while (*lptr) { /* skip leading white-space and commas */ while (*lptr && (*lptr == ' ' || *lptr == '\t' || *lptr == '\n' || *lptr == '\r' || *lptr == ',')) lptr++; /* copy into the scratch buffer */ CommaNext = FALSE; zptr = (sptr = abuf) + sizeof(abuf); while (*lptr && *lptr != '\n' && *lptr != '\r' && sptr < zptr) { if (*lptr == '\"') { if (sptr < zptr) *sptr++ = *lptr++; while (*lptr && *lptr != '\n' && *lptr != '\r' && *lptr != '\"' && sptr < zptr) if (*lptr == '\\' && !iscntrl(*(lptr+1))) { *sptr++ = *lptr++; if (sptr < zptr) *sptr++ = *lptr++; } else *sptr++ = *lptr++; if (*lptr == '\"' && sptr < zptr) *sptr++ = *lptr++; CommaNext = TRUE; } else if (*lptr == '<') { if (sptr < zptr) *sptr++ = *lptr++; while (*lptr && *lptr != '\n' && *lptr != '\r' && *lptr != '>' && sptr < zptr) *sptr++ = *lptr++; if (*lptr == '>' && sptr < zptr) *sptr++ = *lptr++; CommaNext = TRUE; } else if (*lptr == '@') { *sptr++ = *lptr++; CommaNext = TRUE; } else if (*lptr == ',' && CommaNext) break; else if (*lptr == '\\' && !iscntrl(*(lptr+1))) { *sptr++ = *lptr++; if (sptr < zptr) *sptr++ = *lptr++; } else *sptr++ = *lptr++; } if (sptr > zptr) return (NULL); *sptr = '\0'; /* if an empty line */ if (!abuf[0]) continue; /* if the configuration specifies unqualified username is RFC address */ if (SoyMailConfig.SmtpDefaultHost) { /* override to VMS Mail using 0:: */ if (!AddressIsRfc(abuf) && !strstr(abuf,"::")) { /* no, create a RFC-style address */ if (sptr < zptr) *sptr++ = '@'; for (cptr = SoyMailConfig.SmtpDefaultHost; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > zptr) return (NULL); *sptr = '\0'; } } ccnt = sptr - abuf; NewListPtr = CgiLibVeeMemRealloc (NewListPtr, NewListSize+ccnt+8); if (!NewListPtr) ErrorExit (vaxc$errno, FI_LI); nlptr = NewListPtr + NewListSize; if (nlptr > NewListPtr) { /* append a comma and newline to previous entry */ if (CommaSeparate) *nlptr++ = ','; *nlptr++ = '\n'; } for (cptr = abuf; *cptr; *nlptr++ = *cptr++); NewListSize = nlptr - NewListPtr; } *nlptr = '\0'; AddressListPurge (NewListPtr); if (WatchEnabled) WatchThis ("MASSAGE to |!AZ", NewListPtr ? NewListPtr : "(null)"); return (NewListPtr); } /*****************************************************************************/ /* Remove exact duplicates from an address list. Assumes it has been massaged into an address list format. Returns the number of characters in the purged list. */ int AddressListPurge (char *ListPtr) { char ch1, ch2; char *cptr, *lptr, *sptr, *tptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddressListPurge() |%s|\n", ListPtr); if (!ListPtr) return (0); if (!(lptr = ListPtr)) return (0); while (*lptr) { /* scan to the end of the next address */ for (cptr = lptr; *cptr && *cptr != '\n'; cptr++); /* if the last address in the list */ if (!*cptr || !*(cptr+1)) { /* eliminate any trailing newline */ *cptr = '\0'; break; } ch1 = *cptr; *cptr++ = '\0'; /* scan through the rest of the list comparing addresses to that one */ while (*cptr) { for (sptr = cptr; *cptr && *cptr != '\n'; cptr++); ch2 = *cptr; *cptr = '\0'; if (!strcmp (lptr, sptr)) { /* same-same, eliminate by copying the rest over the top of it */ *cptr = ch2; if (*cptr) cptr++; for (tptr = sptr; *cptr; *tptr++ = *cptr++); *tptr = '\0'; cptr = sptr; } else { /* not the same, just resume */ *cptr = ch2; if (*cptr) cptr++; sptr = cptr; } } while (*lptr) lptr++; *lptr = ch1; /* if the last address in the list */ if (!*lptr || !*(lptr+1)) { /* eliminate any trailing newline */ *lptr = '\0'; } if (*lptr) lptr++; } if (Debug) fprintf (stdout, "AddressListPurge() |%s|\n", ListPtr); return (lptr - ListPtr); } /*****************************************************************************/ /* Parse an address from a list. Return a pointer to a static buffer containing the address, an empty string if end-of-list, or a NULL if an error was encountered. Update the supplied list pointer ready to parse the next address. */ char* AddressListExtract (char **ListPtrPtr) { static char abuf [256]; char *cptr, *lptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddressListExtract()\n"); if (!ListPtrPtr) return (NULL); if (!(lptr = *ListPtrPtr)) return (NULL); zptr = (sptr = abuf) + sizeof(abuf)-1; *sptr = '\0'; while (*lptr && isspace(*lptr) && *lptr != '\n') lptr++; while (*lptr && *lptr != '\n' && sptr < zptr) *sptr++ = *lptr++; *sptr-- = '\0'; while (sptr > abuf && (isspace(*sptr) || *sptr == ',')) *sptr-- = '\0'; while (*lptr && *lptr != '\n') lptr++; while (*lptr && *lptr == '\n') lptr++; /* update the pointer to the list */ *ListPtrPtr = lptr; if (Debug) fprintf (stdout, "|%s|\n", abuf); return (abuf); } /*****************************************************************************/ /* Does the supplied address look like VMS Mail style. */ BOOL AddressIsVms (char* cptr) { char *aptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddressIsVms() |%s|\n", cptr); for (;;) { aptr = AddressListExtract (&cptr); if (!aptr || !*aptr) { if (Debug) fprintf (stdout, "FALSE\n"); return (FALSE); } while (*aptr) { if (*aptr == '\"') { /* ignore anything inside of quotes */ aptr++; while (*aptr && *aptr != '\"') { if (*aptr == '\\' && *(aptr+1)) aptr += 2; aptr++; } if (*aptr) aptr++; } else { while (*aptr && (isspace(*aptr) || isalnum(*aptr) || *aptr == '_' || *aptr == '$' || *aptr == ':')) { if (*aptr == ':' && *(USHORTPTR)aptr != '::') break; if (*(USHORTPTR)aptr == '::') aptr++; aptr++; } if (*aptr && *aptr != '\"') break; } } if (!*aptr) { /* if through without encountering anything non-VMS */ if (Debug) fprintf (stdout, "TRUE\n"); return (TRUE); } } } /*****************************************************************************/ /* Does the supplied address look like Internet style? */ BOOL AddressIsRfc (char *cptr) { char *aptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddressIsRfc() |%s|\n", cptr); for (;;) { aptr = AddressListExtract (&cptr); if (!aptr || !*aptr) { if (Debug) fprintf (stdout, "FALSE\n"); return (FALSE); } while (*aptr) { if (*aptr == '\"') { /* ignore anything inside of quotes */ aptr++; while (*aptr && *aptr != '\"') { if (*aptr == '\\' && *(aptr+1)) aptr += 2; aptr++; } if (*aptr) aptr++; } else { if (*aptr == '\\' && *(aptr+1)) aptr += 2; /* if it looks like a component of an Internet-style address */ if (*aptr == '<' || *aptr == '@') { if (Debug) fprintf (stdout, "TRUE\n"); return (TRUE); } aptr++; } } } } /*****************************************************************************/