/*****************************************************************************/ /* inetmail.c Functions relating to the processing of RFC822/2822 message and RFC 1521/1522/1523 (MIME) message headers and their fields. COPYRIGHT --------- Copyright (C) 2005-2024 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 --------------- 25-FEB-2018 MGD bugfix; InetMailParseMimeAttrib() span space after equate 18-APR-2013 MGD InetMailParseHeader() allow for a bloody-minded and probably illegal combination of and 22-JUL-2012 WK bugfix; InetMailParseHeader() ignore s at end-of-header 04-AUG-2011 MGD InetMailParseHeader() liberalise RFC822 detection 19-APR-2009 MGD bugfix; InetMailParseHeader() end-of-header detection 12-MAR-2009 MGD bugfix; InetMailParseField() exit from second-guess 23-FEB-2009 MGD InetMailParseField() try to second-guess when a VMS record buffer overflowed due to unbreakable character sequence 10-DEC-2008 MGD InetMailGetField() improve parsing of leading continuation bugfix; InetMailFoldRfc822Header() line folding 05-NOV-2008 MGD bugfix; InetMailParseHeader() empty field from 23-SEP-2008 01-NOV-2008 MGD InetMailParseHeader() "In-Reply-To:" and "References:" 23-SEP-2008 MGD InetMailParseHeader() if a field is empty just ignore it (prompted by an empty reply-to) 18-AUG-2007 MGD InetMailParseHeader() manufacture MIME for message with content-type of HTML but no MIME version header (kosher?) 15-JUL-2007 MGD report RFC header error line number 07-FEB-2007 MGD bugfix; InetMailParseField() handling continuation line after carriage-control ("Sent Items") 25-APR-2006 MGD modify message from derivation slightly 16-MAR-2006 MGD bugfix; InetMailParseHeader() non-MIME MIME-like headers 08-MAR-2006 MGD bugfix; allow MIME related headers (e.g. "Content-type:") to precede the "MIME-version:" header (but cancel MIME if it is not ultimately found in the message header) 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 #include #include /* application header file */ #include #include #define MAIL$M_EXTNSTD 0x40 #define FI_LI __FILE__, __LINE__ /* global storage */ /* prototypes */ /* external storage */ extern BOOL Debug, WatchEnabled; extern int VmsVersion; extern char LocalHostName[], SoftwareId[], SoftwareVn[]; extern VMS_MAIL_USER VmsMailUser; /*****************************************************************************/ /* Parse the RFC822 header getting the values of fields of interest. */ BOOL InetMailParseHeader (VMS_MAIL_MSG *vmptr) { int crcnt, len, lcnt, nlcnt, rfcnt; char *cptr, *fptr, *sptr, *tptr, *zptr; char FieldBuffer [4096]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailParseHeader()\n"); if (!vmptr->BodyPtr) ErrorExit(SS$_BUGCHECK, FI_LI); /* initially set these to the VMS Mail header items */ vmptr->MsgFromPtr = vmptr->From; vmptr->MsgToPtr = vmptr->To; vmptr->MsgCcPtr = vmptr->Cc; vmptr->MsgSubjPtr = vmptr->Subject; vmptr->MsgDatePtr = vmptr->Date; vmptr->MsgBodyPtr = vmptr->BodyPtr; if (vmptr->MessageFlags & MAIL$M_EXTNSTD) { if (Debug) fprintf (stdout, "MAIL$M_EXTNSTD\n"); vmptr->VmsForeign = TRUE; return (FALSE); } lcnt = rfcnt = 0; cptr = vmptr->BodyPtr; while (*cptr) { /* check to see if it looks like a leading RFC822 header */ len = InetMailParseField (cptr); /* if the parse function could not make sense of the text */ if (len == -1) return (FALSE); if (strsame (cptr, "content-type:", 13) || strsame (cptr, "date:", 5) || strsame (cptr, "from:", 5) || strsame (cptr, "message-id:", 11) || strsame (cptr, "mime:", 5) || strsame (cptr, "return-path:", 12) || strsame (cptr, "received:", 9)) rfcnt++; cptr += len; /* break if it's beginning to smell like RFC822 */ if (rfcnt >= 2) break; /* return if it looks like an empty line */ if (*(USHORTPTR)cptr == '\r\n') return (FALSE); if (*cptr == '\n') return (FALSE); /* if we've looked far enough */ if (lcnt++ > INETMAIL_RFC_SEARCH_MAX) return (FALSE); /* otherwise continue looking */ } /* nope, doesn't look like an RFC822 header */ if (!*cptr) return (FALSE); vmptr->LooksLikeRfc = TRUE; cptr = vmptr->BodyPtr; while (*cptr) { /* note the start of this line */ sptr = cptr; len = InetMailParseField (cptr); /* if the parse function could not make sense of the text */ if (len == -1) { vmptr->RfcErrorLineNumber = 1; for (cptr = vmptr->BodyPtr; cptr < sptr; cptr++) if (*cptr == '\n') vmptr->RfcErrorLineNumber++; return (FALSE); } /* adjust to the beginning of the next line */ cptr += len; /* get the field value from the current line */ len = InetMailGetField (sptr, len, FieldBuffer, sizeof(FieldBuffer)); /* if the get function could not make sense of the text */ if (len == -1) { vmptr->RfcErrorLineNumber = 1; for (cptr = vmptr->BodyPtr; cptr < sptr; cptr++) if (*cptr == '\n') vmptr->RfcErrorLineNumber++; return (FALSE); } len = MimeDecIsoString (FieldBuffer); if (len == -1) len = strlen(FieldBuffer); if (len == 0) { /* if the field is empty ignore it! */ if (Debug) fprintf (stdout, "empty field!\n"); } else if (strsame (sptr, "bcc:", 4)) { sptr = vmptr->RfcBccPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcBccLength = len; } else if (strsame (sptr, "cc:", 3)) { sptr = vmptr->RfcCcPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcCcLength = len; } else if (strsame (sptr, "date:", 5)) { sptr = vmptr->RfcDatePtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcDateLength = len; } else if (strsame (sptr, "from:", 5)) { sptr = vmptr->RfcFromPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcFromLength = len; } else if (strsame (sptr, "in-reply-to:", 12)) { sptr = vmptr->RfcInReplyToPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcInReplyToLength = len; } else if (strsame (sptr, "return-path:", 12)) { sptr = vmptr->RfcReturnPathPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcReturnPathLength = len; } else if (strsame (sptr, "message-id:", 11)) { sptr = vmptr->RfcMessageIdPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcMessageIdLength = len; } else if (strsame (sptr, "references:", 11)) { sptr = vmptr->RfcReferencesPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcReferencesLength = len; } else if (strsame (sptr, "reply-to:", 9)) { sptr = vmptr->RfcReplyToPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcReplyToLength = len; } else if (strsame (sptr, "subject:", 8)) { sptr = vmptr->RfcSubjPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcSubjLength = len; } else if (strsame (sptr, "to:", 3)) { sptr = vmptr->RfcToPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->RfcToLength = len; } else if (strsame (sptr, "X-VMS-To:", 9)) { sptr = vmptr->XVmsToPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); strcpy (sptr, FieldBuffer); vmptr->XVmsToLength = len; } else if (strsame (sptr, "MIME-version:", 13) || strsame (sptr, "content-type:", 13) || strsame (sptr, "content-length:", 15) || strsame (sptr, "content-location:", 17) || strsame (sptr, "content-disposition:", 20) || strsame (sptr, "content-description:", 20) || strsame (sptr, "content-transfer-encoding:", 26)) { /* all MIME related header fields */ if (!vmptr->MimeDataPtr) { vmptr->MimeDataPtr = (MIME_DATA*) CgiLibVeeMemCalloc (sizeof(MIME_DATA)); if (!vmptr->MimeDataPtr) ErrorExit (vaxc$errno, FI_LI); } InetMailParseMimeHeader (vmptr->MimeDataPtr, sptr, FieldBuffer); } /* count consecutative-ish new lines */ nlcnt = crcnt = 0; for (tptr = cptr; nlcnt < 2 && (*tptr == '\n' || *tptr == '\r'); tptr++) { if (*tptr == '\n') nlcnt += 1; crcnt += 1; } if (nlcnt == 2) { if (Debug) fprintf (stdout, "End of header |%d|\n", crcnt); cptr += crcnt; break; } } vmptr->RfcHeaderPtr = vmptr->BodyPtr; vmptr->RfcHeaderLength = cptr - vmptr->BodyPtr; vmptr->RfcBodyPtr = cptr; vmptr->RfcBodyLength = vmptr->BodyLength - vmptr->RfcHeaderLength; if (vmptr->MimeDataPtr) { if (vmptr->MimeDataPtr->VersionNumber) { vmptr->MimeDataPtr->PartContentPtr = vmptr->RfcBodyPtr; vmptr->MimeDataPtr->PartContentLength = vmptr->RfcBodyLength; } else if (vmptr->MimeDataPtr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) { /* had a content-type header of HTML but no MIME header */ vmptr->MimeDataPtr->ManufacturedMime = TRUE; vmptr->MimeDataPtr->PartContentPtr = vmptr->RfcBodyPtr; vmptr->MimeDataPtr->PartContentLength = vmptr->RfcBodyLength; vmptr->MimeDataPtr->PartCount = 1; } else { /* without a usable "MIME-version:" header it aint gonna stay MIME! */ vmptr->MimeDataPtr = NULL; } } if (!(vmptr->MsgFromPtr = vmptr->RfcFromPtr)) if (!(vmptr->MsgFromPtr = vmptr->RfcReplyToPtr)) vmptr->MsgFromPtr = ""; if (!(vmptr->MsgToPtr = vmptr->RfcToPtr)) vmptr->MsgToPtr = ""; if (!(vmptr->MsgCcPtr = vmptr->RfcCcPtr)) vmptr->MsgCcPtr = ""; if (!(vmptr->MsgSubjPtr = vmptr->RfcSubjPtr)) vmptr->MsgSubjPtr = ""; if (!(vmptr->MsgDatePtr = vmptr->RfcDatePtr)) vmptr->MsgDatePtr = ""; if (!(vmptr->MsgBodyPtr = vmptr->RfcBodyPtr)) vmptr->MsgBodyPtr = ""; return (TRUE); } /*****************************************************************************/ /* Parse a header field from the text. This can be a continued header field. Return the number of characters to the last character of that field (includes the carriage-control of that line). This section of characters may contain embedded carriage control if header field continuation line(s) were encountered and contains the trailing carriage control. Return -1 if the header does not seem to make sense. */ int InetMailParseField (char *TextPtr) { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailParseField()\n"); cptr = TextPtr; while (*cptr) { /* scan the end of the current line */ while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++; /* should see carriage-control */ if (!*cptr) return (-1); /* if it's an empty line (the end-of-header) */ if (cptr - TextPtr <= 0) return (0); if (*cptr == '\n') { if (*(cptr+1) == '\n') return (cptr-TextPtr); /* if a bloody-minded combination of and */ if (*(USHORTPTR)(cptr+1) == '\r\n') return (cptr-TextPtr); cptr++; /* if it's not a continuation line */ if (!ISLWS(*cptr)) { /* second-guess VMS line buffer 'overflow' */ if (cptr - TextPtr < 255) return (cptr-TextPtr); /* look ahead for something like a header name */ for (sptr = cptr; *sptr && *sptr != ':' && *sptr != '\n' && sptr-cptr < 64; sptr++); if (*sptr == ':') return (cptr-TextPtr); /* nope, looks like a pesky 'overflow' situation */ } } else if (*(USHORTPTR)cptr == '\r\n') { if (*(USHORTPTR)(cptr+2) == '\r\n') return (cptr-TextPtr); cptr += 2; /* if it's not a continuation line */ if (!ISLWS(*cptr)) { /* second-guess VMS line buffer 'overflow' */ if (cptr - TextPtr < 255) return (cptr-TextPtr); /* look ahead for something like a header name */ for (sptr = cptr; *sptr && *sptr != ':' && *sptr != '\n' && sptr-cptr < 64; sptr++); if (*sptr == ':') return (cptr-TextPtr); /* nope, looks like a pesky 'overflow' situation */ } } else { /* doesn't make sense */ return (-1); } /* continuation line, skip leading white-space */ while (*cptr && ISLWS(*cptr)) cptr++; } return (-1); } /*****************************************************************************/ /* Using the length determined by InetMailParseField() get that field's value from the text placing it in 'FieldBuffer' and returning the length of that value. return -1 on error. */ int InetMailGetField ( char *TextPtr, int FieldLength, char *FieldBuffer, int FieldBufferSize ) { int FieldNameLength; char *cptr, *czptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailGetField()\n"); czptr = (cptr = TextPtr) + FieldLength; *(sptr = FieldBuffer) = '\0'; /* if there is no field (will happen) */ if (ISLWS(*cptr) || *cptr == '\r' || *cptr == '\n') return (0); zptr = sptr + FieldBufferSize; /* skip over the field name and past any leading white-space */ while (*cptr && cptr < czptr && *cptr != ':' && *cptr != '\r' && *cptr != '\n') cptr++; /* if there is an error in the message then indicate */ if (*cptr == '\r' || *cptr == '\n') return (-1); if (!*cptr || cptr >= czptr) return (-1); cptr++; FieldNameLength = cptr - TextPtr; while (*cptr && cptr < czptr && ISLWS(*cptr)) cptr++; /* get the field value */ while (*cptr && cptr < czptr && sptr < zptr) { if (*cptr != '\r' && *cptr != '\n') { *sptr++ = *cptr++; continue; } while (*cptr && cptr < czptr && (*cptr == '\r' || *cptr == '\n' || ISLWS(*cptr))) cptr++; if (sptr > FieldBuffer) { /* replace continuation white-space with a single space character */ if (*cptr && cptr < czptr && sptr < zptr) *sptr++ = ' '; } } /* on overflow just return an empty field value */ if (sptr >= zptr) sptr = FieldBuffer; *sptr = '\0'; if (WatchEnabled) WatchThis ("HEADER (!3UL) !#AZ !AZ", sptr - FieldBuffer, FieldNameLength, TextPtr, FieldBuffer); return (sptr - FieldBuffer); } /*****************************************************************************/ /* Parse the contents of a MIME-related header field into the specified MIME data structure. If the MIME data structure has not been allocated then create one. */ int InetMailParseMimeHeader ( MIME_DATA *mdptr, char *FieldName, char *FieldBuffer ) { int len; char *cptr, *fnptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailParseMimeHeader() |%132.132s|%s|\n", FieldName, FieldBuffer); if (!mdptr) ErrorExit (SS$_BUGCHECK, FI_LI); /* if it's an 'empty field' then just finalise */ if (!FieldBuffer[0]) return (0); cptr = FieldBuffer; if (strsame (FieldName, fnptr = "MIME-version:", 13)) { if (!memcmp (cptr, "1.0", 3)) mdptr->VersionNumber = 10; else mdptr->VersionNumber = 0; while (*cptr && (isdigit(*cptr) || *cptr == '.')) cptr++; } else if (strsame (FieldName, fnptr = "Content-Type:", 13)) { for (cptr = sptr = FieldBuffer; *sptr && !isspace(*sptr) && *sptr != ';'; sptr++); len = sptr - FieldBuffer; sptr = mdptr->ContentTypePtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); while (len--) *sptr++ = *cptr++; *sptr = '\0'; if (strsame (mdptr->ContentTypePtr, "application/octet-stream", -1)) mdptr->ContentTypeIs = MIME_CONTENT_OCTET_STREAM; else if (strsame (mdptr->ContentTypePtr, "multipart/mixed", 16)) { mdptr->ContentTypeIs = MIME_CONTENT_MULTIPART_MIX; mdptr->ContentTypeIsMultipart = TRUE; } else if (strsame (mdptr->ContentTypePtr, "multipart/alternative", 21)) { mdptr->ContentTypeIs = MIME_CONTENT_MULTIPART_ALT; mdptr->ContentTypeIsMultipart = TRUE; } else if (strsame (mdptr->ContentTypePtr, "multipart/", 10)) { mdptr->ContentTypeIs = MIME_CONTENT_MULTIPART; mdptr->ContentTypeIsMultipart = TRUE; } else if (strsame (mdptr->ContentTypePtr, "text/plain", -1)) mdptr->ContentTypeIs = MIME_CONTENT_TEXT_PLAIN; else if (strsame (mdptr->ContentTypePtr, "text/html", -1)) mdptr->ContentTypeIs = MIME_CONTENT_TEXT_HTML; else if (strsame (mdptr->ContentTypePtr, "message/rfc822", 14)) mdptr->ContentTypeIs = MIME_CONTENT_MESSAGE_RFC822; else mdptr->ContentTypeIs = MIME_CONTENT_OTHER; /* just in case, ensure these are reset */ mdptr->BoundaryPtr = NULL; mdptr->BoundaryLength = 0; } else if (strsame (FieldName, fnptr = "Content-Disposition:", 20)) { for (cptr = sptr = FieldBuffer; *sptr && !isspace(*sptr) && *sptr != ';'; sptr++); len = sptr - FieldBuffer; sptr = mdptr->ContentDispositionPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); while (len--) *sptr++ = *cptr++; *sptr = '\0'; } else if (strsame (FieldName, fnptr = "Content-Description:", 20)) { for (cptr = sptr = FieldBuffer; *sptr && !isspace(*sptr) && *sptr != ';'; sptr++); len = sptr - FieldBuffer; sptr = mdptr->ContentDescriptionPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); while (len--) *sptr++ = *cptr++; *sptr = '\0'; } else if (strsame (FieldName, fnptr = "Content-Location:", 17)) { for (cptr = sptr = FieldBuffer; *sptr && !isspace(*sptr) && *sptr != ';'; sptr++); len = sptr - FieldBuffer; sptr = mdptr->ContentLocationPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); while (len--) *sptr++ = *cptr++; *sptr = '\0'; } else if (strsame (FieldName, fnptr = "Content-Transfer-Encoding:", 26)) { for (cptr = sptr = FieldBuffer; *sptr && !isspace(*sptr) && *sptr != ';'; sptr++); len = sptr - FieldBuffer; sptr = mdptr->TransferEncodingPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); while (len--) *sptr++ = *cptr++; *sptr = '\0'; if (strsame (mdptr->TransferEncodingPtr, "7bit", -1)) mdptr->TransferEncodingIs = MIME_TRANSENC_7BIT; else if (strsame (mdptr->TransferEncodingPtr, "8bit", -1) || strsame (mdptr->TransferEncodingPtr, "binary", -1)) mdptr->TransferEncodingIs = MIME_TRANSENC_8BIT; else if (strsame (mdptr->TransferEncodingPtr, "base64", -1)) mdptr->TransferEncodingIs = MIME_TRANSENC_BASE64; else if (strsame (mdptr->TransferEncodingPtr, "quoted-printable", -1)) mdptr->TransferEncodingIs = MIME_TRANSENC_QUOTED; else mdptr->TransferEncodingIs = MIME_TRANSENC_OTHER; } else if (strsame (FieldName, fnptr = "Content-Length:", 15)) { for (sptr = cptr = FieldBuffer; *cptr && isdigit(*cptr); cptr++); len = cptr - sptr; sptr = mdptr->ContentLengthPtr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); cptr = FieldBuffer; while (len--) *sptr++ = *cptr++; *sptr = '\0'; mdptr->ContentLength = atol(mdptr->ContentLengthPtr); } else { if (WatchEnabled) WatchThis ("MIME !UL unknown !48AZ", mdptr->PartCount, FieldName); return (-1); } if (WatchEnabled) WatchThis ("MIME !UL HEADER !AZ !AZ", mdptr->PartCount, fnptr, FieldBuffer); /*********************************/ /* retrieve any field attributes */ /*********************************/ while (*cptr) { while (*cptr && *cptr != ';') cptr++; if (!*cptr) break; cptr++; while (*cptr && ISLWS(*cptr)) cptr++; if (!*cptr) break; if (strsame (cptr, "name=", 5)) cptr += InetMailParseMimeAttrib (mdptr, cptr, &mdptr->NamePtr, NULL); else if (strsame (cptr, "filename=", 9)) cptr += InetMailParseMimeAttrib (mdptr, cptr, &mdptr->FileNamePtr, NULL); else if (strsame (cptr, "charset=", 8)) cptr += InetMailParseMimeAttrib (mdptr, cptr, &mdptr->CharSetPtr, NULL); else if (strsame (cptr, "format=", 7)) cptr += InetMailParseMimeAttrib (mdptr, cptr, &mdptr->FormatPtr, NULL); else if (strsame (cptr, "boundary=", 9)) { cptr += InetMailParseMimeAttrib (mdptr, cptr, &mdptr->BoundaryPtr, &mdptr->BoundaryLength); if (mdptr->BoundaryPtr && mdptr->BoundaryLength) { /* rebuild the boundary to include the two leading hyphens */ sptr = CgiLibVeeMemCalloc (mdptr->BoundaryLength+3); if (!sptr) ErrorExit (vaxc$errno, FI_LI); *sptr++ = '-'; *sptr++ = '-'; strcpy (sptr, mdptr->BoundaryPtr); mdptr->BoundaryPtr = sptr-2; mdptr->BoundaryLength += 2; } } else { /* just skip over this unknown one */ cptr += InetMailParseMimeAttrib (mdptr, cptr, NULL, NULL); } } if (mdptr->CharSetPtr && strsame (mdptr->CharSetPtr, "unicode-1-1-utf-7", -1)) { /* don't know how to do this yet and causes all sorts of strangeness! */ if (WatchEnabled) WatchThis ("*** UNICODE-1-1-UTF-7 ***"); mdptr->CharSetPtr = SOY_DEFAULT_CHARSET; } return (0); } /*****************************************************************************/ /* Parse a 'name=value;' or 'name="value";' attribute from a mime header field. If the attribute is to be stored then allocate dynamic memory for the value string and adjust the supplied attribute pointer and optionally length. Return the number of characters parse across the string. */ int InetMailParseMimeAttrib ( MIME_DATA *mdptr, char *StringPtr, char **AttribPtrPtr, int *AttribLengthPtr ) { int len; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailParseMimeAttrib() |%s|\n", StringPtr); if (!StringPtr) ErrorExit (SS$_BUGCHECK, FI_LI); for (cptr = StringPtr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; while (*cptr && isspace(*cptr)) cptr++; if (*cptr == '\"') { cptr++; for (sptr = cptr; *sptr && *sptr != '\"'; *sptr++); len = sptr - cptr; if (AttribPtrPtr) { sptr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); memcpy (sptr, cptr, len); sptr[len] = '\0'; } cptr = cptr + len; if (*cptr == '\"') cptr++; } else { for (sptr = cptr; *sptr && !isspace(*sptr) && *sptr != ';'; *sptr++); len = sptr - cptr; if (AttribPtrPtr) { sptr = CgiLibVeeMemCalloc (len+1); if (!sptr) ErrorExit (vaxc$errno, FI_LI); memcpy (sptr, cptr, len); sptr[len] = '\0'; } cptr = cptr + len; } len = MimeDecIsoString (sptr); if (len == -1) len = strlen(sptr); if (AttribPtrPtr) *AttribPtrPtr = sptr; if (AttribLengthPtr) *AttribLengthPtr = len; if (WatchEnabled) WatchThis ("MIME !UL ATTRIBUTE !#AZ", mdptr->PartCount, cptr-StringPtr, StringPtr); return (cptr - StringPtr); } /*****************************************************************************/ /* Strip anything looking like 'TRANS%""' from the supplied string and return a pointer to an allocated string containing the stripped results. */ char* InetMailStripTransport (char *address) { char *cptr, *sptr, *zptr; char buffer [4096]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailStripTransport() |%s|\n", address); zptr = (sptr = buffer) + sizeof(buffer)-1; cptr = address; while (*cptr) { if (*(USHORTPTR)cptr == '%\"') { if (sptr > buffer) sptr--; while (sptr > buffer && (isalnum(*sptr) || *sptr == '_' || *sptr == '$')) sptr--; if (sptr > buffer) sptr++; cptr += 2; while (*cptr && *cptr != '\"') { if (sptr < zptr) *sptr++ = *cptr; cptr++; } if (*cptr) cptr++; } else { if (sptr < zptr) *sptr++ = *cptr; cptr++; } } *sptr = '\0'; sptr = CgiLibVeeMemCalloc (sptr-buffer+1); strcpy (sptr, buffer); return (sptr); } /*****************************************************************************/ /* Process a VMS-style date/time string in the format "31-MAR-2005 20:35:16.32" returning a pointer to an allocated string in the RFC/Unix-style format "31 Mar 2005 20:35:16". */ char* InetMailRfcVmsDate (char *VmsDateString) { int cnt, dd, yyyy, hh, mm, ss; char *cptr, *sptr; char mon [32], RfcDateString [64]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailRfcVmsDate() |%s|\n", VmsDateString); cnt = sscanf (VmsDateString, "%2d-%3s-%4d %2d:%2d:%2d", &dd, &mon, &yyyy, &hh, &mm, &ss); if (cnt != 6) cnt = 0; if (dd < 1 || dd > 31) cnt = 0; if (strlen(mon) != 3) cnt = 0; if (yyyy < 1970 || yyyy > 2099) cnt = 0; if (hh < 0 || hh > 24) cnt = 0; if (mm < 0 || mm > 59) cnt = 0; if (ss < 0 || ss > 59) cnt = 0; if (cnt) { mon[0] = toupper(mon[0]); mon[1] = tolower(mon[1]); mon[2] = tolower(mon[2]); cnt = sprintf (cptr = RfcDateString, "%02d %s %d %02d:%02d:%02d", dd, mon, yyyy, hh, mm, ss); } else cnt = strlen(cptr = VmsDateString); sptr = CgiLibVeeMemCalloc (cnt+1); memcpy (sptr, cptr, cnt); return (sptr); } /*****************************************************************************/ /* Ensure an RFC822 header line does not exceed maximum allowed length. */ int InetMailFoldRfc822Header ( char *FieldBuffer, int FieldBufferSize, char *FieldName, char *FieldValue ) { int cnt; char *aptr, *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "InetMailFoldRfc822Header() %d |%s|%s|\n", FieldBufferSize, FieldName, FieldValue); /* if field value string is NULL or empty return zero length */ if (!FieldValue) return (0); for (cptr = FieldValue; *cptr && isspace(*cptr); cptr++); if (!*cptr) return (0); zptr = (sptr = FieldBuffer) + FieldBufferSize; for (cptr = FieldName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = ' '; cptr = FieldValue; while (*cptr && sptr < zptr) { cnt = 0; aptr = cptr; while (*cptr && sptr < zptr) { if (cnt++ > SOY_WRAP_AT) { *sptr = '\0'; while (cptr > aptr && !isspace(*cptr)) { cptr--; sptr--; } if (isspace(*cptr)) { if (sptr < zptr) *sptr++ = '\r'; if (sptr < zptr) *sptr++ = '\n'; if (sptr < zptr) *sptr++ = ' '; cnt = 0; cptr++; } else while (*sptr) { sptr++; cptr++; } aptr = cptr; } if (sptr < zptr) { if (isprint(*cptr)) *sptr++ = *cptr; else if (*cptr == '\n') *sptr++ = ' '; } cptr++; } if (sptr < zptr) *sptr++ = '\r'; if (sptr < zptr) *sptr++ = '\n'; } if (sptr >= zptr) return (-1); *sptr = '\0'; return (sptr - FieldBuffer); } /*****************************************************************************/