/******************************************************************************/ /* mimeenc.c MIME content encoding. Plain-text to quoted-printable. Plain-text to ISO-quoted string. Any data (blob) into base-64. COPYRIGHT --------- Copyright (C) 2005-2016 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 --------------- 24-FEB-2007 MGD MimeEncQuotedPrintable() bugfix for very small texts 04-JUL-2006 MGD MimeEncNeedsQuotedPrintable() line length now 250 15-APR-2005 MGD initial */ /******************************************************************************/ /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include /* application header file */ #include #include #define FI_LI __FILE__, __LINE__ /* global storage */ char MimeEncBase64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz0123456789+/"; #define CHAR64(c) (MimeEncBase64Chars[(unsigned char)(c)]) char MimeEncHexDigits [] = "0123456789ABCDEF"; /* external storage */ extern BOOL Debug; /******************************************************************************/ /* Does this text need to encoded quoted-printable? */ BOOL MimeEncNeedsQuotedPrintable (char *TextPtr) { /* somewhat arbitrary (and now definitely not RFC!) */ #define QP_LONG_LINE 250 char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MimeEncNeedsQuotedPrintable()\n"); for (cptr = sptr = TextPtr; *cptr; cptr++) { /* if an 8 bit character */ if (!isascii(*cptr)) return (1); /* if it's far too long */ if (cptr - sptr > QP_LONG_LINE) return (1); if (*cptr == '\r' || *cptr == '\n') sptr = cptr + 1; } return (0); } /******************************************************************************/ /* Convert plain-text string to quoted-printable. Returns a pointer to dynamic storeage allocated. */ char* MimeEncQuotedPrintable ( char *string, int length ) { #define QP_LINE_LEN 78 char *bptr, *cptr, *lptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MimeEncQuotedPrintable() |%s|\n", string); if (length <= 0) length = strlen(string); for (;;) { /* allow 20% for encoding */ length += length / 5; if (length < 256) length = 256; bptr = CgiLibVeeMemCalloc (length); if (!bptr) exit (vaxc$errno); zptr = (sptr = bptr) + length-1; lptr = sptr + QP_LINE_LEN; cptr = string; while (*cptr && sptr < zptr) { if ((*cptr >= 33 && *cptr <= 60) || (*cptr >= 62 && *cptr <= 126) || *cptr == 10 || *cptr == 13) { if (sptr >= lptr) { if (sptr < zptr) *sptr++ = '='; if (sptr < zptr) *sptr++ = '\n'; lptr = sptr + QP_LINE_LEN; } *sptr++ = *cptr; if (*cptr == 13) lptr = sptr + QP_LINE_LEN; cptr++; } else if ((*cptr == 9 || *cptr == 32) && *(cptr+1) != 10 && *(cptr+1) != 13) { if (sptr >= lptr) { if (sptr < zptr) *sptr++ = '='; if (sptr < zptr) *sptr++ = '\n'; lptr = sptr + QP_LINE_LEN; } *sptr++ = *cptr++; } else { if (sptr+3 >= lptr) { if (sptr < zptr) *sptr++ = '='; if (sptr < zptr) *sptr++ = '\n'; lptr = sptr + QP_LINE_LEN; } *sptr++ = '='; if (sptr < zptr) *sptr++ = MimeEncHexDigits[(*cptr & 0xf0) >> 4]; if (sptr < zptr) *sptr++ = MimeEncHexDigits[*cptr & 0x0f]; cptr++; } } *sptr = '\0'; /* break if the buffer didn't overflow */ if (sptr < zptr) break; CgiLibVeeMemFree (bptr); } if (Debug) fprintf (stdout, "%d |%s|\n", sptr-bptr, bptr); return (bptr); } /******************************************************************************/ /* Convert any data to base-64. Allocates storage to parameter length plus 40% and populates. Returns pointer to same, or NULL if there was some issue during the encoding. The carriage control would 'normally' be "\r\n" but to generate without any can be "", or even "\n". */ char* MimeEncBase64 ( char *DataPtr, int DataLength, char *CarCon ) { int ccnt, llen, EncodedSize; unsigned char c1, c2, c3; char *cptr, *ccptr, *czptr, *sptr, *zptr, *EncodedPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MimeEncBase64() %d\n", DataLength); /* base-64 requires about 40% more storage than the original data */ if (DataLength >= 128) EncodedSize = DataLength + (DataLength / 2); else EncodedSize = DataLength + DataLength + DataLength; if (Debug) fprintf (stdout, "%d\n", EncodedSize); EncodedPtr = CgiLibVeeMemCalloc (EncodedSize+16); if (!EncodedPtr) ErrorExit (vaxc$errno, FI_LI); zptr = (sptr = EncodedPtr) + EncodedSize; czptr = (cptr = DataPtr) + DataLength; llen = 0; while (cptr < czptr && sptr < zptr) { ccnt = 1; c1 = (unsigned char)*cptr++; if (cptr < czptr) { c2 = (unsigned char)*cptr++; ccnt++; } else c2 = 0; if (cptr < czptr) { c3 = (unsigned char)*cptr++; ccnt++; } else c3 = 0; *sptr++ = CHAR64 (c1>>2); *sptr++ = CHAR64 (((c1 & 0x3)<< 4) | ((c2 & 0xf0) >> 4)); if (ccnt == 1) { *sptr++ = '='; *sptr++ = '='; } else if (ccnt == 2) { *sptr++ = CHAR64 (((c2 & 0x0f) << 2) | ((c3 & 0xc0) >> 6)); *sptr++ = '='; } else { *sptr++ = CHAR64 (((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6)); *sptr++ = CHAR64 (c3 & 0x3f); } llen += 4; if (llen > 71) { for (ccptr = CarCon; *ccptr && sptr < zptr; *sptr++ = *ccptr++); llen = 0; } } if (llen) for (ccptr = CarCon; *ccptr && sptr < zptr; *sptr++ = *ccptr++); *sptr = '\0'; if (sptr >= zptr) { if (Debug) fprintf (stdout, "OVERFLOW!\n"); return (NULL); } return (EncodedPtr); } /******************************************************************************/ /* Convert plain to ISO-quoted, first checks to ensure it's needed. Any character from 1 to 127 (isascii()) is included as-is, only characters with the eighth bit set are encoded. Then encodes each space-delimitted 'word'. By rights this encoded word should be limited to 76 characters. This function does not enforce that. */ char* MimeEncIsoString ( char *String, char *CharSet ) { char *cptr, *sptr, *tptr, *zptr; char IsoBuffer [4096]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MimeEncIsoString() |%s|\n", String); /* first check if it contains any '8 bit' characters */ for (cptr = String; *cptr && isascii(*cptr); cptr++); /* nope */ if (!*cptr) return (String); if (!CharSet || !CharSet[0]) CharSet = SOY_DEFAULT_CHARSET; zptr = (sptr = IsoBuffer) + sizeof(IsoBuffer)-1; cptr = String; while (*cptr && sptr < zptr) { /* copy leading/intervening white-space */ while (*cptr && (isascii(*cptr) && isspace(*cptr)) && sptr < zptr) *sptr++ = *cptr++; /* look ahead across this 'word' for a character that needs encoding */ for (tptr = cptr; *tptr && isascii(*tptr) && !isspace(*tptr); tptr++); /* if no character requires encoding then just copy it straight up! */ if (!*tptr || (isascii(*tptr) && isspace(*tptr))) { while (*cptr && !(isascii(*cptr) && isspace(*cptr)) && sptr < zptr) *sptr++ = *cptr++; continue; } /* needs encoding */ if (sptr < zptr) *sptr++ = '='; if (sptr < zptr) *sptr++ = '?'; for (tptr = CharSet; *tptr && sptr < zptr; *sptr++ = *tptr++); if (sptr < zptr) *sptr++ = '?'; if (sptr < zptr) *sptr++ = 'Q'; if (sptr < zptr) *sptr++ = '?'; while (*cptr && !(isascii(*cptr) && isspace(*cptr)) && sptr < zptr) { if (isascii(*cptr)) *sptr++ = *cptr++; else { *sptr++ = '='; if (sptr < zptr) *sptr++ = MimeEncHexDigits[(*cptr & 0xf0) >> 4]; if (sptr < zptr) *sptr++ = MimeEncHexDigits[*cptr & 0x0f]; cptr++; } } if (sptr < zptr) *sptr++ = '?'; if (sptr < zptr) *sptr++ = '='; } if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; cptr = CgiLibVeeMemCalloc ((sptr-IsoBuffer)+1); if (!cptr) ErrorExit (vaxc$errno, FI_LI); strcpy (cptr, IsoBuffer); if (Debug) fprintf (stdout, "|%s|\n", cptr); return (cptr); } /******************************************************************************/