void skipWhite(const char **s) { const char *t = *s; while (isspaceByte(*t)) ++t; *s = t; } /* skipWhite */
void trimWhite(char *s) { int l; if (!s) return; l = strlen(s); while (l && isspaceByte(s[l - 1])) --l; s[l] = 0; } /* trimWhite */
/* compress white space */ void spaceCrunch(char *s, bool onespace, bool unprint) { int i, j; char c; bool space = true; for (i = j = 0; c = s[i]; ++i) { if (isspaceByte(c)) { if (!onespace) continue; if (!space) s[j++] = ' ', space = true; continue; } if (unprint && !isprintByte(c)) continue; s[j++] = c, space = false; } if (space && j) --j; /* drop trailing space */ s[j] = 0; } /* spaceCrunch */
/* Return 0 if there was no need to encode */ static char *isoEncode(char *start, char *end) { int nacount = 0, count = 0, len; char *s, *t; char c, code; for (s = start; s < end; ++s) { c = *s; if (c == 0) *s = ' '; if (isspaceByte(c)) *s = ' '; } for (s = start; s < end; ++s) { c = *s; ++count; if (!isprintByte(c) && c != ' ') ++nacount; } if (!nacount) return 0; if (nacount * 4 >= count && count > 8) { code = 'B'; s = base64Encode(start, end - start, false); goto package; } code = 'Q'; s = qpEncode(start); package: len = strlen(s); t = allocMem(len + 20); sprintf(t, "=?ISO-8859-1?%c?%s?=", code, s); nzFree(s); return t; } /* isoEncode */
/* read and/or refresh the address book */ bool loadAddressBook(void) { char *buf, *bufend, *v, *last, *s, *t; bool cmt = false; char state = 0, c; int j, buflen, ln = 1; time_t mtime; if (!addressFile || (mtime = fileTimeByName(addressFile)) == -1 || mtime <= adbooktime) return true; debugPrint(3, "loading address book"); nzFree(addressList); addressList = 0; nads = 0; if (!fileIntoMemory(addressFile, &buf, &buflen)) return false; bufend = buf + buflen; for (s = t = last = buf; s < bufend; ++s) { c = *s; if (cmt) { if (c != '\n') continue; cmt = false; } if (c == ':') { /* delimiter */ if (state == 0) { setError(MSG_ABNoAlias, ln); freefail: nzFree(buf); nads = 0; return false; } while (t[-1] == ' ' || t[-1] == '\t') --t; if (state == 1) { *t++ = c; state = 2; continue; } c = '#'; /* extra fields are ignored */ } /* : */ if (c == '#') { cmt = true; continue; } if (c == '\n') { ++ln; if (state == 0) continue; if (state == 1) { setError(MSG_ABNoColon, ln - 1); goto freefail; } if (state == 3) { ++nads; while (isspaceByte(t[-1])) --t; *t = 0; v = strchr(last, ':'); if (v - last >= 16) { setError(MSG_ABAliasLong, ln - 1); goto freefail; } ++v; if (t - v >= 64) { setError(MSG_ABMailLong, ln - 1); goto freefail; } if (!strchr(v, '@')) { setError(MSG_ABNoAt, ln - 1); goto freefail; } if (strpbrk(v, " \t")) { setError(MSG_ABMailSpaces, ln - 1); goto freefail; } while (last < t) { if (!isprintByte(*last)) { setError(MSG_AbMailUnprintable, ln - 1); goto freefail; } ++last; } *t++ = c; } else t = last; /* back it up */ last = t; state = 0; continue; } /* nl */ if ((c == ' ' || c == '\t') && (state == 0 || state == 2)) continue; if (state == 0) state = 1; if (state == 2) state = 3; *t++ = c; } *t = 0; if (state) { setError(MSG_ABUnterminated); goto freefail; } if (nads) { addressList = allocMem(nads * sizeof(struct ALIAS)); j = 0; for (s = buf; *s; s = t + 1, ++j) { t = strchr(s, ':'); memcpy(addressList[j].name, s, t - s); addressList[j].name[t - s] = 0; s = t + 1; t = strchr(s, '\n'); memcpy(addressList[j].email, s, t - s); addressList[j].email[t - s] = 0; } } /* aliases are present */ nzFree(buf); adbooktime = mtime; return true; } /* loadAddressBook */
/* Read a file into memory, mime encode it, * and return the type of encoding and the encoded data. * Last three parameters are result parameters. * If ismail is nonzero, the file is the mail, not an attachment. * In fact ismail indicates the line that holds the subject. * If ismail is negative, then -ismail indicates the subject line, * and the string file is not the filename, but rather, the mail to send. */ bool encodeAttachment(const char *file, int ismail, bool webform, const char **type_p, const char **enc_p, char **data_p) { char *buf; char c; bool longline; char *s, *t, *v; char *ct, *ce; /* content type, content encoding */ int buflen, i, cx; int nacount, nullcount, nlcount; if (ismail < 0) { buf = cloneString(file); buflen = strlen(buf); ismail = -ismail; file = EMPTYSTRING; } else { if (!ismc && (cx = stringIsNum(file)) >= 0) { static char newfilename[16]; if (!unfoldBuffer(cx, false, &buf, &buflen)) return false; if (!buflen) { if (webform) { empty: buf = EMPTYSTRING; ct = "text/plain"; ce = "7bit"; goto success; } setError(MSG_BufferXEmpty, cx); goto freefail; } sprintf(newfilename, "<buffer %d>", cx); file = newfilename; if (sessionList[cx].lw->fileName) file = sessionList[cx].lw->fileName; } else { if (!fileIntoMemory(file, &buf, &buflen)) return false; if (!buflen) { if (webform) goto empty; setError(MSG_FileXEmpty, file); goto freefail; } } } /* ismail negative or normal */ if (ismail) { /* Put newline at the end. Yes, the buffer is allocated * with space for newline and null. */ if (buf[buflen - 1] != '\n') buf[buflen++] = '\n'; /* check for subject: line */ s = buf; i = ismail; while (--i) { while (*s != '\n') ++s; ++s; } while (*s == ' ' || *s == '\t') ++s; if (!memEqualCI(s, "subject:", 8)) { setError(MSG_SubjectStart); goto freefail; } s += 8; while (*s == ' ' || *s == '\t') ++s; t = s; while (*s != '\n') ++s; v = s; while (s > t && isspaceByte(s[-1])) --s; if (s - t >= sizeof(subjectLine)) { setError(MSG_SubjectLong, sizeof(subjectLine) - 1); goto freefail; } if (s > t) memcpy(subjectLine, t, s - t); subjectLine[s - t] = 0; if (subjectLine[0]) { char *subjiso = isoEncode(subjectLine, subjectLine + strlen(subjectLine)); if (subjiso) { if (strlen(subjiso) >= sizeof(subjectLine)) { nzFree(subjiso); setError(MSG_SubjectLong, sizeof(subjectLine) - 1); goto freefail; } strcpy(subjectLine, subjiso); nzFree(subjiso); } } debugPrint(6, "subject = %s", subjectLine); /* Blank lines after subject are optional, and ignored. */ for (t = buf + buflen; v < t; ++v) if (*v != '\r' && *v != '\n') break; buflen -= (v - buf); if (buflen) memmove(buf, v, buflen); buf[buflen] = 0; if (doSignature) { /* Append .signature file. */ /* Try account specific .signature file, then fall back to .signature */ sprintf(sigFileEnd, "%d", mailAccount); c = fileTypeByName(sigFile, false); if (!c) { *sigFileEnd = 0; c = fileTypeByName(sigFile, false); } if (c != 0) { int fd, n; if (c != 'f') { setError(MSG_SigRegular); goto freefail; } n = fileSizeByName(sigFile); if (n > 0) { buf = reallocMem(buf, buflen + n + 1); fd = open(sigFile, O_RDONLY); if (fd < 0) { setError(MSG_SigAccess); goto freefail; } read(fd, buf + buflen, n); close(fd); buflen += n; buf[buflen] = 0; } } } /* .signature */ } /* Infer content type from the filename */ ct = 0; s = strrchr(file, '.'); if (s && s[1]) { ++s; if (stringEqualCI(s, "ps")) ct = "application/PostScript"; if (stringEqualCI(s, "jpeg")) ct = "image/jpeg"; if (stringEqualCI(s, "gif")) ct = "image/gif"; if (stringEqualCI(s, "wav")) ct = "audio/basic"; if (stringEqualCI(s, "mpeg")) ct = "video/mpeg"; if (stringEqualCI(s, "rtf")) ct = "text/richtext"; if (stringEqualCI(s, "htm") || stringEqualCI(s, "html") || stringEqualCI(s, "shtm") || stringEqualCI(s, "shtml") || stringEqualCI(s, "asp")) ct = "text/html"; } /* Count the nonascii characters */ nacount = nullcount = nlcount = 0; longline = false; s = 0; for (i = 0; i < buflen; ++i) { c = buf[i]; if (c == '\0') ++nullcount; if (c < 0) ++nacount; if (c != '\n') continue; ++nlcount; t = buf + i; if (s && t - s > 120) longline = true; if (!s && i > 120) longline = true; s = t; } t = buf + i; if (s && t - s > 120) longline = true; if (!s && i > 120) longline = true; debugPrint(6, "attaching %s length %d nonascii %d nulls %d longline %d", file, buflen, nacount, nullcount, longline); nacount += nullcount; /* Set the type of attachment */ if (buflen > 20 && nacount * 5 > buflen) { if (!ct) ct = "application/octet-stream"; /* default type for binary */ } if (!ct) ct = "text/plain"; /* Criteria for base64 encode. * files uploaded from a web form need not be encoded, unless they contain * nulls, which is a quirk of my slapped together software. */ if (!webform && (buflen > 20 && nacount * 5 > buflen) || webform && nullcount) { if (ismail) { setError(MSG_MailBinary, file); goto freefail; } s = base64Encode(buf, buflen, true); nzFree(buf); buf = s; ce = "base64"; goto success; } if (!webform) { /* Switch to unix newlines - we'll switch back to dos later. */ v = buf + buflen; for (s = t = buf; s < v; ++s) { c = *s; if (c == '\r' && s < v - 1 && s[1] == '\n') continue; *t++ = c; } buflen = t - buf; /* Do we need to use quoted-printable? */ /* Perhaps this hshould read (nacount > 0) */ if (nacount * 20 > buflen || nullcount || longline) { char *newbuf; int l, colno = 0, space = 0; newbuf = initString(&l); v = buf + buflen; for (s = buf; s < v; ++s) { c = *s; /* do we have to =expand this character? */ if (c < '\n' && c != '\t' || c == '=' || c == '\xff' || (c == ' ' || c == '\t') && s < v - 1 && s[1] == '\n') { char expand[4]; sprintf(expand, "=%02X", (uchar) c); stringAndString(&newbuf, &l, expand); colno += 3; } else { stringAndChar(&newbuf, &l, c); ++colno; } if (c == '\n') { colno = space = 0; continue; } if (c == ' ' || c == '\t') space = l; if (colno < 72) continue; if (s == v - 1) continue; /* If newline's coming up anyways, don't force another one. */ if (s[1] == '\n') continue; i = l; if (!space || space == i) { stringAndString(&newbuf, &l, "=\n"); colno = space = 0; continue; } colno = i - space; stringAndString(&newbuf, &l, "**"); /* make room */ while (i > space) { newbuf[i + 1] = newbuf[i - 1]; --i; } newbuf[space] = '='; newbuf[space + 1] = '\n'; space = 0; } /* loop over characters */ nzFree(buf); buf = newbuf; ce = "quoted-printable"; goto success; } } buf[buflen] = 0; ce = (nacount ? "8bit" : "7bit"); success: debugPrint(6, "encoded %s %s length %d", ct, ce, strlen(buf)); *enc_p = ce; *type_p = ct; *data_p = buf; return true; freefail: nzFree(buf); return false; } /* encodeAttachment */
static void isoDecode(char *vl, char **vrp) { char *vr = *vrp; char *start, *end; /* section being decoded */ char *s, *t, c, d, code; int len; uchar val, leftover, mod; start = vl; restart: start = strstr(start, "=?"); if (!start || start >= vr) goto finish; start += 2; if (!memEqualCI(start, "iso-", 4) && !memEqualCI(start, "us-ascii", 8) && !memEqualCI(start, "utf-", 4) && !memEqualCI(start, "cp1252", 6) && !memEqualCI(start, "gb", 2) && !memEqualCI(start, "windows-", 8)) goto restart; s = strchr(start, '?'); if (!s || s > vr - 5 || s[2] != '?') goto restart; code = s[1]; code = toupper(code); if (code != 'Q' && code != 'B') goto restart; s += 3; end = strstr(s, "?="); if (!end || end > vr - 2) goto restart; t = start - 2; if (code == 'Q') { while (s < end) { c = *s++; if (c == '=') { c = *s; d = s[1]; if (isxdigit(c) && isxdigit(d)) { d = fromHex(c, d); *t++ = d; s += 2; continue; } c = '='; } *t++ = c; } goto copy; } /* base64 */ mod = 0; for (; s < end; ++s) { c = *s; if (isspaceByte(c)) continue; if (c == '=') continue; val = base64Bits(c); if (val & 64) val = 0; /* ignore errors here */ if (mod == 0) { leftover = val << 2; } else if (mod == 1) { *t++ = (leftover | (val >> 4)); leftover = val << 4; } else if (mod == 2) {