/* Before we render a mail message, let's make sure it looks like email. * This is similar to htmlTest() in html.c. */ bool emailTest(void) { int i, j, k, n; /* This is a very simple test - hopefully not too simple. * The first 20 non-indented lines have to look like mail header lines, * with at least half the keywords recognized. */ for (i = 1, j = k = 0; i <= cw->dol && j < 20; ++i) { char *q; char *p = (char *)fetchLine(i, -1); char first = *p; if (first == '\n' || first == '\r' && p[1] == '\n') break; if (first == ' ' || first == '\t') continue; ++j; /* nonindented line */ for (q = p; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ; if (q == p) continue; if (*q++ != ':') continue; /* X-Whatever is a mail header word */ if (q - p >= 8 && p[1] == '-' && toupper(p[0]) == 'X') { ++k; } else { for (n = 0; mhwords[n]; ++n) if (memEqualCI(mhwords[n], p, q - p)) break; if (mhwords[n]) ++k; } if (k >= 4 && k * 2 >= j) return true; } /* loop over lines */ return false; } /* emailTest */
/* set document.cookie to the cookies relevant to this url */ static void docCookie(jsobjtype d) { int cook_l; char *cook = initString(&cook_l); const char *url = cw->fileName; bool secure = false; const char *proto; char *s; if (url) { proto = getProtURL(url); if (proto && stringEqualCI(proto, "https")) secure = true; sendCookies(&cook, &cook_l, url, secure); if (memEqualCI(cook, "cookie: ", 8)) { /* should often happen */ strmove(cook, cook + 8); } if (s = strstr(cook, "\r\n")) *s = 0; } set_property_string(d, "cookie", cook); nzFree(cook); } /* docCookie */
/* 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 */
bool sendMailCurrent(int sm_account, bool dosig) { const char *reclist[MAXRECAT + 1]; char *recmem; const char *atlist[MAXRECAT + 1]; char *atmem; char *s, *t; char cxbuf[4]; int lr, la, ln; char *refline = 0; int nrec = 0, nat = 0, nalt = 0; int account = localAccount; int j; bool rc = false; bool subj = false; if (cw->browseMode) { setError(MSG_MailBrowse); return false; } if (cw->sqlMode) { setError(MSG_MailDB); return false; } if (cw->dirMode) { setError(MSG_MailDir); return false; } if (cw->binMode) { setError(MSG_MailBinary2); return false; } if (!cw->dol) { setError(MSG_MailEmpty); return false; } if (!validAccount(account)) return false; recmem = initString(&lr); atmem = initString(&la); /* Gather recipients and attachments, until we reach subject: */ for (ln = 1; ln <= cw->dol; ++ln) { char *line = (char *)fetchLine(ln, -1); if (memEqualCI(line, "to:", 3) || memEqualCI(line, "mailto:", 7) || memEqualCI(line, "cc:", 3) || memEqualCI(line, "bcc:", 4) || memEqualCI(line, "reply to:", 9) || memEqualCI(line, "reply to ", 9)) { char cc = 0; if (toupper(line[0]) == 'C') cc = '^'; if (toupper(line[0]) == 'B') cc = '?'; if (toupper(line[0]) == 'R') line += 9; else line = strchr(line, ':') + 1; while (*line == ' ' || *line == '\t') ++line; if (*line == '\n') { setError(MSG_RecipNone2, ln); goto done; } if (nrec == MAXRECAT) { setError(MSG_RecipMany, MAXRECAT); goto done; } ++nrec; for (t = line; *t != '\n'; ++t) ; if (cc) { if (!lr) { setError(MSG_MailFirstCC); goto done; } stringAndChar(&recmem, &lr, cc); } stringAndBytes(&recmem, &lr, line, t + 1 - line); continue; } if (memEqualCI(line, "attach:", 7) || memEqualCI(line, "alt:", 4)) { if (toupper(line[1]) == 'T') line += 7; else line += 4, ++nalt; while (*line == ' ' || *line == '\t') ++line; if (*line == '\n') { setError(MSG_AttLineX, ln); goto done; } if (nat == MAXRECAT) { setError(MSG_RecipMany, MAXRECAT); goto done; } ++nat; for (t = line; *t != '\n'; ++t) ; stringAndBytes(&atmem, &la, line, t + 1 - line); continue; } if (memEqualCI(line, "account:", 8)) { line += 8; while (*line == ' ' || *line == '\t') ++line; if (!isdigitByte(*line) || (account = strtol(line, &line, 10)) == 0 || account > maxAccount || *line != '\n') { setError(MSG_MailAccountBadLineX, ln); goto done; } continue; } if (memEqualCI(line, "references:", 11)) { if (!refline) refline = line; continue; } if (memEqualCI(line, "subject:", 8)) { while (*line == ' ' || *line == '\t') ++line; subj = true; } break; } /* loop over lines */ if (sm_account) account = sm_account; if (!subj) { setError(((ln > cw->dol) + MSG_MailFirstLine), ln); goto done; } if (nrec == 0) { setError(MSG_RecipNone3); goto done; } for (s = recmem, j = 0; *s; s = t + 1, ++j) { t = strchr(s, '\n'); *t = 0; reclist[j] = s; } reclist[j] = 0; for (s = atmem, j = 0; *s; s = t + 1, ++j) { t = strchr(s, '\n'); *t = 0; atlist[j] = s; } atlist[j] = 0; sprintf(cxbuf, "%d", context); rc = sendMail(account, reclist, cxbuf, ln, atlist, refline, nalt, dosig); done: nzFree(recmem); nzFree(atmem); if (!rc && intFlag) setError(MSG_Interrupted); if (rc) i_puts(MSG_OK); return rc; } /* sendMailCurrent */
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) {
/* Look for the name of the attachment and boundary */ static void ctExtras(struct MHINFO *w, const char *s, const char *t) { char quote; const char *q, *al, *ar; if (w->ct < CT_MULTI) { quote = 0; for (q = s + 1; q < t; ++q) { if (isalnumByte(q[-1])) continue; /* could be name= or filename= */ if (memEqualCI(q, "file", 4)) q += 4; if (!memEqualCI(q, "name=", 5)) continue; q += 5; if (*q == '"') { quote = *q; ++q; } for (al = q; q < t; ++q) { if (*q == '"') break; if (quote) continue; if (strchr(",; \t", *q)) break; } ar = q; if (ar - al >= MHLINE) ar = al + MHLINE - 1; strncpy(w->cfn, al, ar - al); break; } } /* regular file */ if (w->ct >= CT_MULTI) { quote = 0; for (q = s + 1; q < t; ++q) { if (isalnumByte(q[-1])) continue; if (!memEqualCI(q, "boundary=", 9)) continue; q += 9; if (*q == '"') { quote = *q; ++q; } for (al = q; q < t; ++q) { if (*q == '"') break; if (quote) continue; if (strchr(",; \t", *q)) break; } ar = q; w->boundlen = ar - al; strncpy(w->boundary, al, ar - al); break; } } /* multi or alt */ } /* ctExtras */