/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)send.c 2.85 (gritter) 6/16/07"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include /* * Mail -- a mail program * * Mail to mail folders and displays. */ enum parseflags { PARSE_DEFAULT = 0, PARSE_DECRYPT = 01, PARSE_PARTS = 02 }; static void onpipe(int signo); extern void brokpipe(int signo); static int sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf, struct ignoretab *doign, char *prefix, size_t prefixlen, enum sendaction action, off_t *stats, int level); static struct mimepart *parsemsg(struct message *mp, enum parseflags pf); static enum okay parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part); static void endpart(struct mimepart **np, off_t xoffs, long lines); static void parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static void parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level); static size_t out(char *buf, size_t len, FILE *fp, enum conversion convert, enum sendaction action, char *prefix, size_t prefixlen, off_t *stats, char **restp, size_t *restsizep); static void addstats(off_t *stats, off_t lines, off_t bytes); static FILE *newfile(struct mimepart *ip, int *ispipe, sighandler_type *oldpipe); static char *getpipecmd(char *content); static FILE *getpipefile(char *cmd, FILE **qbuf, int quote); static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, char *prefix, size_t prefixlen, off_t *stats); static void statusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats); static void xstatusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats); static void put_from_(FILE *fp, struct mimepart *ip); static sigjmp_buf pipejmp; /*ARGSUSED*/ static void onpipe(int signo) { siglongjmp(pipejmp, 1); } /* * Send message described by the passed pointer to the * passed output buffer. Return -1 on error. * Adjust the status: field if need be. * If doign is given, suppress ignored header fields. * prefix is a string to prepend to each output line. * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT). * stats[0] is line count, stats[1] is character count. stats may be NULL. * Note that stats[0] is valid for SEND_MBOX only. */ int send(struct message *mp, FILE *obuf, struct ignoretab *doign, char *prefix, enum sendaction action, off_t *stats) { size_t count; FILE *ibuf; size_t prefixlen, sz; int c; enum parseflags pf; struct mimepart *ip; char *cp, *cp2; if (mp == dot && action != SEND_TOSRCH && action != SEND_TOFLTR) did_print_dot = 1; if (stats) stats[0] = stats[1] = 0; /* * Compute the prefix string, without trailing whitespace */ if (prefix != NULL) { cp2 = 0; for (cp = prefix; *cp; cp++) if (!blankchar(*cp & 0377)) cp2 = cp; prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; } else prefixlen = 0; /* * First line is the From_ line, so no headers there to worry about. */ if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) return -1; count = mp->m_size; sz = 0; if (mp->m_flag & MNOFROM) { if (doign != allignore && doign != fwdignore && action != SEND_RFC822) sz = fprintf(obuf, "%sFrom %s %s\n", prefix ? prefix : "", fakefrom(mp), fakedate(mp->m_time)); } else { if (prefix && doign != allignore && doign != fwdignore && action != SEND_RFC822) { fputs(prefix, obuf); sz += strlen(prefix); } while (count && (c = getc(ibuf)) != EOF) { if (doign != allignore && doign != fwdignore && action != SEND_RFC822) { putc(c, obuf); sz++; } count--; if (c == '\n') break; } } if (sz) addstats(stats, 1, sz); pf = 0; if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) pf |= PARSE_DECRYPT|PARSE_PARTS; if ((ip = parsemsg(mp, pf)) == NULL) return -1; return sendpart(mp, ip, obuf, doign, prefix, prefixlen, action, stats, 0); } static int sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf, struct ignoretab *doign, char *prefix, size_t prefixlen, enum sendaction action, off_t *stats, int level) { char *line = NULL; size_t linesize = 0, linelen, count, len; int dostat, infld = 0, ignoring = 1, isenc; char *cp, *cp2, *start; int c; struct mimepart *np; FILE *ibuf = NULL, *pbuf = obuf, *qbuf = obuf, *origobuf = obuf; char *tcs, *pipecmd = NULL; enum conversion convert; sighandler_type oldpipe = SIG_DFL; int rt = 0; long lineno = 0; int ispipe = 0; char *rest; size_t restsize; int eof; (void)&ibuf; (void)&pbuf; (void)&convert; (void)&oldpipe; (void)&rt; (void)&obuf; (void)&stats; (void)&action; if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart && action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) goto skip; dostat = 0; if (level == 0) { if (doign != NULL) { if (!is_ign("status", 6, doign)) dostat |= 1; if (!is_ign("x-status", 8, doign)) dostat |= 2; } else dostat = 3; } if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return -1; count = ip->m_size; if (ip->m_mimecontent == MIME_DISCARD) goto skip; if ((ip->m_flag&MNOFROM) == 0) while (count && (c = getc(ibuf)) != EOF) { count--; if (c == '\n') break; } isenc = 0; convert = action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH || action == SEND_TOFLTR ? CONV_FROMHDR : CONV_NONE; while (foldergets(&line, &linesize, &count, &linelen, ibuf)) { lineno++; if (line[0] == '\n') { /* * If line is blank, we've reached end of * headers, so force out status: field * and note that we are no longer in header * fields */ if (dostat & 1) statusput(zmp, obuf, prefix, stats); if (dostat & 2) xstatusput(zmp, obuf, prefix, stats); if (doign != allignore) out("\n", 1, obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); break; } isenc &= ~1; if (infld && blankchar(line[0]&0377)) { /* * If this line is a continuation (via space or tab) * of a previous header field, determine if the start * of the line is a MIME encoded word. */ if (isenc & 2) { for (cp = line; blankchar(*cp&0377); cp++); if (cp > line && linelen - (cp - line) > 8 && cp[0] == '=' && cp[1] == '?') isenc |= 1; } } else { /* * Pick up the header field if we have one. */ for (cp = line; (c = *cp&0377) && c != ':' && !spacechar(c); cp++); cp2 = cp; while (spacechar(*cp&0377)) cp++; if (cp[0] != ':' && level == 0 && lineno == 1) { /* * Not a header line, force out status: * This happens in uucp style mail where * there are no headers at all. */ if (dostat & 1) statusput(zmp, obuf, prefix, stats); if (dostat & 2) xstatusput(zmp, obuf, prefix, stats); if (doign != allignore) out("\n", 1, obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); break; } /* * If it is an ignored field and * we care about such things, skip it. */ c = *cp2; *cp2 = 0; /* temporarily null terminate */ if (doign && is_ign(line, cp2 - line, doign)) ignoring = 1; else if (asccasecmp(line, "status") == 0) { /* * If the field is "status," go compute * and print the real Status: field */ if (dostat & 1) { statusput(zmp, obuf, prefix, stats); dostat &= ~1; ignoring = 1; } } else if (asccasecmp(line, "x-status") == 0) { /* * If the field is "status," go compute * and print the real Status: field */ if (dostat & 2) { xstatusput(zmp, obuf, prefix, stats); dostat &= ~2; ignoring = 1; } } else ignoring = 0; *cp2 = c; infld = 1; } /* * Determine if the end of the line is a MIME encoded word. */ isenc &= ~2; if (count && (c = getc(ibuf)) != EOF) { if (blankchar(c)) { if (linelen > 0 && line[linelen-1] == '\n') cp = &line[linelen-2]; else cp = &line[linelen-1]; while (cp >= line && whitechar(*cp&0377)) cp++; if (cp - line > 8 && cp[0] == '=' && cp[-1] == '?') isenc |= 2; } ungetc(c, ibuf); } if (!ignoring) { start = line; len = linelen; if (action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH || action == SEND_TOFLTR) { /* * Strip blank characters if two MIME-encoded * words follow on continuing lines. */ if (isenc & 1) while (len>0&&blankchar(*start&0377)) { start++; len--; } if (isenc & 2) if (len > 0 && start[len-1] == '\n') len--; while (len > 0 && blankchar(start[len-1]&0377)) len--; } out(start, len, obuf, convert, action, prefix, prefixlen, stats, NULL, NULL); if (ferror(obuf)) { free(line); return -1; } } } free(line); line = NULL; skip: switch (ip->m_mimecontent) { case MIME_822: switch (action) { case SEND_TOFLTR: putc('\0', obuf); /*FALLTHRU*/ case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: put_from_(obuf, ip->m_multipart); /*FALLTHRU*/ case SEND_TOSRCH: case SEND_DECRYPT: goto multi; case SEND_TOFILE: case SEND_TOPIPE: put_from_(obuf, ip->m_multipart); /*FALLTHRU*/ case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } break; case MIME_TEXT_HTML: if (action == SEND_TOFLTR) putc('\b', obuf); /*FALLTHRU*/ case MIME_TEXT: case MIME_TEXT_PLAIN: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: pipecmd = getpipecmd(ip->m_ct_type_plain); /*FALLTHRU*/ default: break; } break; case MIME_DISCARD: if (action != SEND_DECRYPT) return rt; break; case MIME_PKCS7: if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW && ip->m_multipart) goto multi; /*FALLTHRU*/ default: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: if ((pipecmd = getpipecmd(ip->m_ct_type_plain)) != NULL) break; if (level == 0 && count) { cp = "[Binary content]\n\n"; out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); } /*FALLTHRU*/ case SEND_TOFLTR: return rt; case SEND_TOFILE: case SEND_TOPIPE: case SEND_TOSRCH: case SEND_DECRYPT: case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } break; case MIME_ALTERNATIVE: if ((action == SEND_TODISP || action == SEND_QUOTE) && value("print-alternatives") == NULL) for (np = ip->m_multipart; np; np = np->m_nextpart) if (np->m_mimecontent == MIME_TEXT_PLAIN) { if (sendpart(zmp, np, obuf, doign, prefix, prefixlen, action, stats, level+1) < 0) return -1; return rt; } /*FALLTHRU*/ case MIME_MULTI: case MIME_DIGEST: switch (action) { case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE: case SEND_QUOTE_ALL: case SEND_TOFILE: case SEND_TOPIPE: case SEND_TOSRCH: case SEND_TOFLTR: case SEND_DECRYPT: multi: if ((action == SEND_TODISP || action == SEND_TODISP_ALL) && ip->m_multipart != NULL && ip->m_multipart->m_mimecontent == MIME_DISCARD && ip->m_multipart->m_nextpart == NULL) { cp = "[Missing multipart boundary - " "use \"show\" to display " "the raw message]\n\n"; out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); } for (np = ip->m_multipart; np; np = np->m_nextpart) { if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT) continue; switch (action) { case SEND_TOFILE: if (np->m_partstring && strcmp(np->m_partstring, "1") == 0) break; stats = NULL; if ((obuf = newfile(np, &ispipe, &oldpipe)) == NULL) continue; break; case SEND_TODISP: case SEND_TODISP_ALL: case SEND_QUOTE_ALL: if ((ip->m_mimecontent == MIME_MULTI || ip->m_mimecontent == MIME_ALTERNATIVE || ip->m_mimecontent == MIME_DIGEST) && np->m_partstring) { len = strlen(np->m_partstring) + 40; cp = ac_alloc(len); snprintf(cp, len, "%sPart %s:\n", level || strcmp(np->m_partstring, "1") ? "\n" : "", np->m_partstring); out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, prefix, prefixlen, stats, NULL, NULL); ac_free(cp); } break; case SEND_TOFLTR: putc('\0', obuf); /*FALLTHRU*/ case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: case SEND_TOSRCH: case SEND_QUOTE: case SEND_DECRYPT: case SEND_TOPIPE: break; } if (sendpart(zmp, np, obuf, doign, prefix, prefixlen, action, stats, level+1) < 0) rt = -1; else if (action == SEND_QUOTE) break; if (action == SEND_TOFILE && obuf != origobuf) { if (ispipe == 0) Fclose(obuf); else { safe_signal(SIGPIPE, SIG_IGN); Pclose(obuf); safe_signal(SIGPIPE, oldpipe); } } } return rt; case SEND_MBOX: case SEND_RFC822: case SEND_SHOW: break; } } /* * Copy out message body */ if (doign == allignore && level == 0) /* skip final blank line */ count--; switch (ip->m_mimeenc) { case MIME_BIN: if (stats) stats[0] = -1; /*FALLTHRU*/ case MIME_7B: case MIME_8B: convert = CONV_NONE; break; case MIME_QP: convert = CONV_FROMQP; break; case MIME_B64: switch (ip->m_mimecontent) { case MIME_TEXT: case MIME_TEXT_PLAIN: case MIME_TEXT_HTML: convert = CONV_FROMB64_T; break; default: convert = CONV_FROMB64; } break; default: convert = CONV_NONE; } if (action == SEND_DECRYPT || action == SEND_MBOX || action == SEND_RFC822 || action == SEND_SHOW) convert = CONV_NONE; tcs = gettcharset(); #ifdef HAVE_ICONV if ((action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH) && (ip->m_mimecontent == MIME_TEXT_PLAIN || ip->m_mimecontent == MIME_TEXT_HTML || ip->m_mimecontent == MIME_TEXT)) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if (asccasecmp(tcs, ip->m_charset) && asccasecmp(us_ascii, ip->m_charset)) iconvd = iconv_open_ft(tcs, ip->m_charset); else iconvd = (iconv_t)-1; } #endif /* HAVE_ICONV */ if ((action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL) && pipecmd != NULL) { qbuf = obuf; pbuf = getpipefile(pipecmd, &qbuf, action == SEND_QUOTE || action == SEND_QUOTE_ALL); action = SEND_TOPIPE; if (pbuf != qbuf) { oldpipe = safe_signal(SIGPIPE, onpipe); if (sigsetjmp(pipejmp, 1)) goto end; } } else pbuf = qbuf = obuf; eof = 0; while (!eof && foldergets(&line, &linesize, &count, &linelen, ibuf)) { lineno++; while (convert == CONV_FROMQP && linelen >= 2 && line[linelen-2] == '=') { char *line2; size_t linesize2, linelen2; nextl: line2 = NULL; linesize2 = 0; if (foldergets(&line2, &linesize2, &count, &linelen2, ibuf) == NULL) { eof = 1; break; } if (linelen + linelen2 + 1 > linesize) line = srealloc(line, linesize = linelen + linelen2 + 1); memcpy(&line[linelen], line2, linelen2+1); linelen += linelen2; free(line2); } rest = NULL; restsize = 0; out(line, linelen, pbuf, convert, action, pbuf == origobuf ? prefix : NULL, pbuf == origobuf ? prefixlen : 0, pbuf == origobuf ? stats : NULL, eof ? NULL : &rest, eof ? NULL : &restsize); if (ferror(pbuf)) { rt = -1; break; } if (restsize) { if (line != rest) memmove(line, rest, restsize); linelen = restsize; goto nextl; } } end: free(line); if (pbuf != qbuf) { safe_signal(SIGPIPE, SIG_IGN); Pclose(pbuf); safe_signal(SIGPIPE, oldpipe); if (qbuf != obuf) pipecpy(qbuf, obuf, origobuf, prefix, prefixlen, stats); } #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return rt; } static struct mimepart * parsemsg(struct message *mp, enum parseflags pf) { struct mimepart *ip; ip = csalloc(1, sizeof *ip); ip->m_flag = mp->m_flag; ip->m_have = mp->m_have; ip->m_block = mp->m_block; ip->m_offset = mp->m_offset; ip->m_size = mp->m_size; ip->m_xsize = mp->m_xsize; ip->m_lines = mp->m_lines; ip->m_xlines = mp->m_lines; if (parsepart(mp, ip, pf, 0) != OKAY) return NULL; return ip; } static enum okay parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { char *cp; ip->m_ct_type = hfield("content-type", (struct message *)ip); if (ip->m_ct_type != NULL) { ip->m_ct_type_plain = savestr(ip->m_ct_type); if ((cp = strchr(ip->m_ct_type_plain, ';')) != NULL) *cp = '\0'; } else if (ip->m_parent && ip->m_parent->m_mimecontent == MIME_DIGEST) ip->m_ct_type_plain = "message/rfc822"; else ip->m_ct_type_plain = "text/plain"; ip->m_mimecontent = mime_getcontent(ip->m_ct_type_plain); if (ip->m_ct_type) ip->m_charset = mime_getparam("charset", ip->m_ct_type); if (ip->m_charset == NULL) ip->m_charset = us_ascii; ip->m_ct_transfer_enc = hfield("content-transfer-encoding", (struct message *)ip); ip->m_mimeenc = ip->m_ct_transfer_enc ? mime_getenc(ip->m_ct_transfer_enc) : MIME_7B; if ((cp = hfield("content-disposition", (struct message *)ip)) == 0 || (ip->m_filename = mime_getparam("filename", cp)) == 0) if (ip->m_ct_type != NULL) ip->m_filename = mime_getparam("name", ip->m_ct_type); if (pf & PARSE_PARTS) { if (level > 9999) { fprintf(stderr, "MIME content too deeply nested.\n"); return STOP; } switch (ip->m_mimecontent) { case MIME_PKCS7: if (pf & PARSE_DECRYPT) { parsepkcs7(zmp, ip, pf, level); break; } /*FALLTHRU*/ default: break; case MIME_MULTI: case MIME_ALTERNATIVE: case MIME_DIGEST: parsemultipart(zmp, ip, pf, level); break; case MIME_822: parse822(zmp, ip, pf, level); break; } } return OKAY; } static void parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { char *boundary; char *line = NULL; size_t linesize = 0, linelen, count, boundlen; FILE *ibuf; struct mimepart *np = NULL; off_t offs; int part = 0; long lines = 0; if ((boundary = mime_getboundary(ip->m_ct_type)) == NULL) return; boundlen = strlen(boundary); if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return; count = ip->m_size; while (foldergets(&line, &linesize, &count, &linelen, ibuf)) if (line[0] == '\n') break; offs = ftell(ibuf); newpart(ip, &np, offs, NULL); while (foldergets(&line, &linesize, &count, &linelen, ibuf)) { if ((lines > 0 || part == 0) && linelen >= boundlen + 1 && strncmp(line, boundary, boundlen) == 0) { if (line[boundlen] == '\n') { offs = ftell(ibuf); if (part != 0) { endpart(&np, offs-boundlen-2, lines); newpart(ip, &np, offs-boundlen-2, NULL); } endpart(&np, offs, 2); newpart(ip, &np, offs, &part); lines = 0; } else if (line[boundlen] == '-' && line[boundlen+1] == '-' && line[boundlen+2] == '\n') { offs = ftell(ibuf); if (part != 0) { endpart(&np, offs-boundlen-4, lines); newpart(ip, &np, offs-boundlen-4, NULL); } endpart(&np, offs+count, 2); break; } else lines++; } else lines++; } if (np) { offs = ftell(ibuf); endpart(&np, offs, lines); } for (np = ip->m_multipart; np; np = np->m_nextpart) if (np->m_mimecontent != MIME_DISCARD) parsepart(zmp, np, pf, level+1); free(line); } static void newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part) { struct mimepart *pp; size_t sz; *np = csalloc(1, sizeof **np); (*np)->m_flag = MNOFROM; (*np)->m_have = HAVE_HEADER|HAVE_BODY; (*np)->m_block = mailx_blockof(offs); (*np)->m_offset = mailx_offsetof(offs); if (part) { (*part)++; sz = ip->m_partstring ? strlen(ip->m_partstring) : 0; sz += 20; (*np)->m_partstring = salloc(sz); if (ip->m_partstring) snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part); else snprintf((*np)->m_partstring, sz, "%u", *part); } else (*np)->m_mimecontent = MIME_DISCARD; (*np)->m_parent = ip; if (ip->m_multipart) { for (pp = ip->m_multipart; pp->m_nextpart; pp = pp->m_nextpart); pp->m_nextpart = *np; } else ip->m_multipart = *np; } static void endpart(struct mimepart **np, off_t xoffs, long lines) { off_t offs; offs = mailx_positionof((*np)->m_block, (*np)->m_offset); (*np)->m_size = (*np)->m_xsize = xoffs - offs; (*np)->m_lines = (*np)->m_xlines = lines; *np = NULL; } static void parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { int c, lastc = '\n'; size_t count; FILE *ibuf; off_t offs; struct mimepart *np; long lines; if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) return; count = ip->m_size; lines = ip->m_lines; while (count && ((c = getc(ibuf)) != EOF)) { count--; if (c == '\n') { lines--; if (lastc == '\n') break; } lastc = c; } offs = ftell(ibuf); np = csalloc(1, sizeof *np); np->m_flag = MNOFROM; np->m_have = HAVE_HEADER|HAVE_BODY; np->m_block = mailx_blockof(offs); np->m_offset = mailx_offsetof(offs); np->m_size = np->m_xsize = count; np->m_lines = np->m_xlines = lines; np->m_partstring = ip->m_partstring; np->m_parent = ip; ip->m_multipart = np; substdate((struct message *)np); np->m_from = fakefrom((struct message *)np); parsepart(zmp, np, pf, level+1); } static void parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf, int level) { struct message m, *xmp; struct mimepart *np; char *to, *cc; memcpy(&m, ip, sizeof m); to = hfield("to", zmp); cc = hfield("cc", zmp); if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) { np = csalloc(1, sizeof *np); np->m_flag = xmp->m_flag; np->m_have = xmp->m_have; np->m_block = xmp->m_block; np->m_offset = xmp->m_offset; np->m_size = xmp->m_size; np->m_xsize = xmp->m_xsize; np->m_lines = xmp->m_lines; np->m_xlines = xmp->m_xlines; np->m_partstring = ip->m_partstring; if (parsepart(zmp, np, pf, level+1) == OKAY) { np->m_parent = ip; ip->m_multipart = np; } } } static size_t out(char *buf, size_t len, FILE *fp, enum conversion convert, enum sendaction action, char *prefix, size_t prefixlen, off_t *stats, char **restp, size_t *restsizep) { size_t sz, n; char *cp; long lines; sz = 0; if (action == SEND_MBOX || action == SEND_DECRYPT) { cp = buf; n = len; while (n && cp[0] == '>') cp++, n--; if (n >= 5 && cp[0] == 'F' && cp[1] == 'r' && cp[2] == 'o' && cp[3] == 'm' && cp[4] == ' ') { putc('>', fp); sz++; } } sz += mime_write(buf, len, fp, action == SEND_MBOX ? SEND_MBOX : convert, action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL ? TD_ISPR|TD_ICONV : action == SEND_TOSRCH || action == SEND_TOPIPE ? TD_ICONV : action == SEND_TOFLTR ? TD_DELCTRL : action == SEND_SHOW ? TD_ISPR : TD_NONE, prefix, prefixlen, restp, restsizep); lines = 0; if (stats && stats[0] != -1) { for (cp = buf; cp < &buf[sz]; cp++) if (*cp == '\n') lines++; } addstats(stats, lines, sz); return sz; } static void addstats(off_t *stats, off_t lines, off_t bytes) { if (stats) { if (stats[0] >= 0) stats[0] += lines; stats[1] += bytes; } } /* * Get a file for an attachment. */ static FILE * newfile(struct mimepart *ip, int *ispipe, sighandler_type *oldpipe) { char *f = ip->m_filename; struct str in, out; FILE *fp; *ispipe = 0; if (f != NULL && f != (char *)-1) { in.s = f; in.l = strlen(f); mime_fromhdr(&in, &out, TD_ISPR); memcpy(f, out.s, out.l); *(f + out.l) = '\0'; free(out.s); } if (value("interactive") != NULL) { printf("Enter filename for part %s (%s)", ip->m_partstring ? ip->m_partstring : "?", ip->m_ct_type_plain); f = readtty(catgets(catd, CATSET, 173, ": "), f != (char *)-1 ? f : NULL); } if (f == NULL || f == (char *)-1) return NULL; if (*f == '|') { char *cp; cp = value("SHELL"); if (cp == NULL) cp = SHELL; fp = Popen(f+1, "w", cp, 1); if (fp == NULL) { perror(f); fp = stdout; } else { *oldpipe = safe_signal(SIGPIPE, brokpipe); *ispipe = 1; } } else { if ((fp = Fopen(f, "w")) == NULL) fprintf(stderr, "Cannot open %s\n", f); } return fp; } static char * getpipecmd(char *content) { char *penv, *cp, *cq, *pipecmd; if (content == NULL) return NULL; penv = ac_alloc(strlen(content) + 6); strcpy(penv, "pipe-"); cp = &penv[5]; cq = content; do *cp++ = lowerconv(*cq & 0377); while (*cq++); pipecmd = value(penv); ac_free(penv); return pipecmd; } static FILE * getpipefile(char *pipecmd, FILE **qbuf, int quote) { char *shell; FILE *rbuf = *qbuf; if (pipecmd != NULL) { if (quote) { char *tempPipe; if ((*qbuf = Ftemp(&tempPipe, "Rp", "w+", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 173, "tmpfile")); *qbuf = rbuf; } unlink(tempPipe); Ftfree(&tempPipe); } if ((shell = value("SHELL")) == NULL) shell = SHELL; if ((rbuf = Popen(pipecmd, "W", shell, fileno(*qbuf))) == NULL) { perror(pipecmd); } else { fflush(*qbuf); if (*qbuf != stdout) fflush(stdout); } } return rbuf; } static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, char *prefix, size_t prefixlen, off_t *stats) { char *line = NULL; size_t linesize = 0, linelen, sz, count; fflush(pipebuf); rewind(pipebuf); count = fsize(pipebuf); while (fgetline(&line, &linesize, &count, &linelen, pipebuf, 0) != NULL) { sz = prefixwrite(line, sizeof *line, linelen, outbuf, prefix, prefixlen); if (outbuf == origobuf) addstats(stats, 1, sz); } if (line) free(line); fclose(pipebuf); } /* * Output a reasonable looking status field. */ static void statusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats) { char statout[3]; char *cp = statout; if (mp->m_flag & MREAD) *cp++ = 'R'; if ((mp->m_flag & MNEW) == 0) *cp++ = 'O'; *cp = 0; if (statout[0]) fprintf(obuf, "%sStatus: %s\n", prefix == NULL ? "" : prefix, statout); addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 9 + cp - statout); } static void xstatusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats) { char xstatout[4]; char *xp = xstatout; if (mp->m_flag & MFLAGGED) *xp++ = 'F'; if (mp->m_flag & MANSWERED) *xp++ = 'A'; if (mp->m_flag & MDRAFTED) *xp++ = 'T'; *xp = 0; if (xstatout[0]) fprintf(obuf, "%sX-Status: %s\n", prefix == NULL ? "" : prefix, xstatout); addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 11 + xp - xstatout); } static void put_from_(FILE *fp, struct mimepart *ip) { time_t now; if (ip && ip->m_from) fprintf(fp, "From %s %s\n", ip->m_from, fakedate(ip->m_time)); else { time(&now); fprintf(fp, "From %s %s", myname, ctime(&now)); } } /* * This is fgetline for mbox lines. */ char * foldergets(char **s, size_t *size, size_t *count, size_t *llen, FILE *stream) { char *p, *top; if ((p = fgetline(s, size, count, llen, stream, 0)) == NULL) return NULL; if (*p == '>') { p++; while (*p == '>') p++; if (strncmp(p, "From ", 5) == 0) { /* we got a masked From line */ top = &(*s)[*llen]; p = *s; do p[0] = p[1]; while (++p < top); (*llen)--; } } return *s; }