aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/upas/send/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/upas/send/message.c')
-rw-r--r--src/cmd/upas/send/message.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/src/cmd/upas/send/message.c b/src/cmd/upas/send/message.c
new file mode 100644
index 00000000..eab6160c
--- /dev/null
+++ b/src/cmd/upas/send/message.c
@@ -0,0 +1,573 @@
+#include "common.h"
+#include "send.h"
+
+#include "../smtp/smtp.h"
+#include "../smtp/y.tab.h"
+
+/* global to this file */
+static Reprog *rfprog;
+static Reprog *fprog;
+
+#define VMLIMIT (64*1024)
+#define MSGLIMIT (128*1024*1024)
+
+int received; /* from rfc822.y */
+
+static String* getstring(Node *p);
+static String* getaddr(Node *p);
+
+extern int
+default_from(message *mp)
+{
+ char *cp, *lp;
+
+ cp = getenv("upasname");
+ lp = getlog();
+ if(lp == nil)
+ return -1;
+
+ if(cp && *cp)
+ s_append(mp->sender, cp);
+ else
+ s_append(mp->sender, lp);
+ s_append(mp->date, thedate());
+ return 0;
+}
+
+extern message *
+m_new(void)
+{
+ message *mp;
+
+ mp = (message *)mallocz(sizeof(message), 1);
+ if (mp == 0) {
+ perror("message:");
+ exit(1);
+ }
+ mp->sender = s_new();
+ mp->replyaddr = s_new();
+ mp->date = s_new();
+ mp->body = s_new();
+ mp->size = 0;
+ mp->fd = -1;
+ return mp;
+}
+
+extern void
+m_free(message *mp)
+{
+ if(mp->fd >= 0){
+ close(mp->fd);
+ sysremove(s_to_c(mp->tmp));
+ s_free(mp->tmp);
+ }
+ s_free(mp->sender);
+ s_free(mp->date);
+ s_free(mp->body);
+ s_free(mp->havefrom);
+ s_free(mp->havesender);
+ s_free(mp->havereplyto);
+ s_free(mp->havesubject);
+ free((char *)mp);
+}
+
+/* read a message into a temp file , return an open fd to it */
+static int
+m_read_to_file(Biobuf *fp, message *mp)
+{
+ int fd;
+ int n;
+ String *file;
+ char buf[4*1024];
+
+ file = s_new();
+ /*
+ * create temp file to be remove on close
+ */
+ abspath("mtXXXXXX", UPASTMP, file);
+ mktemp(s_to_c(file));
+ if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
+ s_free(file);
+ return -1;
+ }
+ mp->tmp = file;
+
+ /*
+ * read the rest into the temp file
+ */
+ while((n = Bread(fp, buf, sizeof(buf))) > 0){
+ if(write(fd, buf, n) != n){
+ close(fd);
+ return -1;
+ }
+ mp->size += n;
+ if(mp->size > MSGLIMIT){
+ mp->size = -1;
+ break;
+ }
+ }
+
+ mp->fd = fd;
+ return 0;
+}
+
+/* get the first address from a node */
+static String*
+getaddr(Node *p)
+{
+ for(; p; p = p->next)
+ if(p->s && p->addr)
+ return s_copy(s_to_c(p->s));
+}
+
+/* get the text of a header line minus the field name */
+static String*
+getstring(Node *p)
+{
+ String *s;
+
+ s = s_new();
+ if(p == nil)
+ return s;
+
+ for(p = p->next; p; p = p->next){
+ if(p->s){
+ s_append(s, s_to_c(p->s));
+ }else{
+ s_putc(s, p->c);
+ s_terminate(s);
+ }
+ if(p->white)
+ s_append(s, s_to_c(p->white));
+ }
+ return s;
+}
+
+#if 0 /* jpc */
+static char *fieldname[] =
+{
+[WORD-WORD] "WORD",
+[DATE-WORD] "DATE",
+[RESENT_DATE-WORD] "RESENT_DATE",
+[RETURN_PATH-WORD] "RETURN_PATH",
+[FROM-WORD] "FROM",
+[SENDER-WORD] "SENDER",
+[REPLY_TO-WORD] "REPLY_TO",
+[RESENT_FROM-WORD] "RESENT_FROM",
+[RESENT_SENDER-WORD] "RESENT_SENDER",
+[RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO",
+[SUBJECT-WORD] "SUBJECT",
+[TO-WORD] "TO",
+[CC-WORD] "CC",
+[BCC-WORD] "BCC",
+[RESENT_TO-WORD] "RESENT_TO",
+[RESENT_CC-WORD] "RESENT_CC",
+[RESENT_BCC-WORD] "RESENT_BCC",
+[REMOTE-WORD] "REMOTE",
+[PRECEDENCE-WORD] "PRECEDENCE",
+[MIMEVERSION-WORD] "MIMEVERSION",
+[CONTENTTYPE-WORD] "CONTENTTYPE",
+[MESSAGEID-WORD] "MESSAGEID",
+[RECEIVED-WORD] "RECEIVED",
+[MAILER-WORD] "MAILER",
+[BADTOKEN-WORD] "BADTOKEN",
+};
+#endif /* jpc */
+
+/* fix 822 addresses */
+static void
+rfc822cruft(message *mp)
+{
+ Field *f;
+ Node *p;
+ String *body, *s;
+ char *cp;
+
+ /*
+ * parse headers in in-core part
+ */
+ yyinit(s_to_c(mp->body), s_len(mp->body));
+ mp->rfc822headers = 0;
+ yyparse();
+ mp->rfc822headers = 1;
+ mp->received = received;
+
+ /*
+ * remove equivalent systems in all addresses
+ */
+ body = s_new();
+ cp = s_to_c(mp->body);
+ for(f = firstfield; f; f = f->next){
+ if(f->node->c == MIMEVERSION)
+ mp->havemime = 1;
+ if(f->node->c == FROM)
+ mp->havefrom = getaddr(f->node);
+ if(f->node->c == SENDER)
+ mp->havesender = getaddr(f->node);
+ if(f->node->c == REPLY_TO)
+ mp->havereplyto = getaddr(f->node);
+ if(f->node->c == TO)
+ mp->haveto = 1;
+ if(f->node->c == DATE)
+ mp->havedate = 1;
+ if(f->node->c == SUBJECT)
+ mp->havesubject = getstring(f->node);
+ if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
+ s = f->node->next->next->s;
+ if(s && (strcmp(s_to_c(s), "bulk") == 0
+ || strcmp(s_to_c(s), "Bulk") == 0))
+ mp->bulk = 1;
+ }
+ for(p = f->node; p; p = p->next){
+ if(p->s){
+ if(p->addr){
+ cp = skipequiv(s_to_c(p->s));
+ s_append(body, cp);
+ } else
+ s_append(body, s_to_c(p->s));
+ }else{
+ s_putc(body, p->c);
+ s_terminate(body);
+ }
+ if(p->white)
+ s_append(body, s_to_c(p->white));
+ cp = p->end+1;
+ }
+ s_append(body, "\n");
+ }
+
+ if(*s_to_c(body) == 0){
+ s_free(body);
+ return;
+ }
+
+ if(*cp != '\n')
+ s_append(body, "\n");
+ s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
+ s_terminate(body);
+
+ firstfield = 0;
+ mp->size += s_len(body) - s_len(mp->body);
+ s_free(mp->body);
+ mp->body = body;
+}
+
+/* read in a message, interpret the 'From' header */
+extern message *
+m_read(Biobuf *fp, int rmail, int interactive)
+{
+ message *mp;
+ Resub subexp[10];
+ char *line;
+ int first;
+ int n;
+
+ mp = m_new();
+
+ /* parse From lines if remote */
+ if (rmail) {
+ /* get remote address */
+ String *sender=s_new();
+
+ if (rfprog == 0)
+ rfprog = regcomp(REMFROMRE);
+ first = 1;
+ while(s_read_line(fp, s_restart(mp->body)) != 0) {
+ memset(subexp, 0, sizeof(subexp));
+ if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
+ if(first == 0)
+ break;
+ if (fprog == 0)
+ fprog = regcomp(FROMRE);
+ memset(subexp, 0, sizeof(subexp));
+ if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
+ break;
+ s_restart(mp->body);
+ append_match(subexp, s_restart(sender), SENDERMATCH);
+ append_match(subexp, s_restart(mp->date), DATEMATCH);
+ break;
+ }
+ append_match(subexp, s_restart(sender), REMSENDERMATCH);
+ append_match(subexp, s_restart(mp->date), REMDATEMATCH);
+ if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){
+ append_match(subexp, mp->sender, REMSYSMATCH);
+ s_append(mp->sender, "!");
+ }
+ first = 0;
+ }
+ s_append(mp->sender, s_to_c(sender));
+
+ s_free(sender);
+ }
+ if(*s_to_c(mp->sender)=='\0')
+ default_from(mp);
+
+ /* if sender address is unreturnable, treat message as bulk mail */
+ if(!returnable(s_to_c(mp->sender)))
+ mp->bulk = 1;
+
+ /* get body */
+ if(interactive && !rmail){
+ /* user typing on terminal: terminator == '.' or EOF */
+ for(;;) {
+ line = s_read_line(fp, mp->body);
+ if (line == 0)
+ break;
+ if (strcmp(".\n", line)==0) {
+ mp->body->ptr -= 2;
+ *mp->body->ptr = '\0';
+ break;
+ }
+ }
+ mp->size = mp->body->ptr - mp->body->base;
+ } else {
+ /*
+ * read up to VMLIMIT bytes (more or less) into main memory.
+ * if message is longer put the rest in a tmp file.
+ */
+ mp->size = mp->body->ptr - mp->body->base;
+ n = s_read(fp, mp->body, VMLIMIT);
+ if(n < 0){
+ perror("m_read");
+ exit(1);
+ }
+ mp->size += n;
+ if(n == VMLIMIT){
+ if(m_read_to_file(fp, mp) < 0){
+ perror("m_read");
+ exit(1);
+ }
+ }
+
+ }
+
+ /*
+ * ignore 0 length messages from a terminal
+ */
+ if (!rmail && mp->size == 0)
+ return 0;
+
+ rfc822cruft(mp);
+
+ return mp;
+}
+
+/* return a piece of message starting at `offset' */
+extern int
+m_get(message *mp, long offset, char **pp)
+{
+ static char buf[4*1024];
+
+ /*
+ * are we past eof?
+ */
+ if(offset >= mp->size)
+ return 0;
+
+ /*
+ * are we in the virtual memory portion?
+ */
+ if(offset < s_len(mp->body)){
+ *pp = mp->body->base + offset;
+ return mp->body->ptr - mp->body->base - offset;
+ }
+
+ /*
+ * read it from the temp file
+ */
+ offset -= s_len(mp->body);
+ if(mp->fd < 0)
+ return -1;
+ if(seek(mp->fd, offset, 0)<0)
+ return -1;
+ *pp = buf;
+ return read(mp->fd, buf, sizeof buf);
+}
+
+/* output the message body without ^From escapes */
+static int
+m_noescape(message *mp, Biobuf *fp)
+{
+ long offset;
+ int n;
+ char *p;
+
+ for(offset = 0; offset < mp->size; offset += n){
+ n = m_get(mp, offset, &p);
+ if(n <= 0){
+ Bflush(fp);
+ return -1;
+ }
+ if(Bwrite(fp, p, n) < 0)
+ return -1;
+ }
+ return Bflush(fp);
+}
+
+/*
+ * Output the message body with '^From ' escapes.
+ * Ensures that any line starting with a 'From ' gets a ' ' stuck
+ * in front of it.
+ */
+static int
+m_escape(message *mp, Biobuf *fp)
+{
+ char *p, *np;
+ char *end;
+ long offset;
+ int m, n;
+ char *start;
+
+ for(offset = 0; offset < mp->size; offset += n){
+ n = m_get(mp, offset, &start);
+ if(n < 0){
+ Bflush(fp);
+ return -1;
+ }
+
+ p = start;
+ for(end = p+n; p < end; p += m){
+ np = memchr(p, '\n', end-p);
+ if(np == 0){
+ Bwrite(fp, p, end-p);
+ break;
+ }
+ m = np - p + 1;
+ if(m > 5 && strncmp(p, "From ", 5) == 0)
+ Bputc(fp, ' ');
+ Bwrite(fp, p, m);
+ }
+ }
+ Bflush(fp);
+ return 0;
+}
+
+static int
+printfrom(message *mp, Biobuf *fp)
+{
+ String *s;
+ int rv;
+
+ if(!returnable(s_to_c(mp->sender)))
+ return Bprint(fp, "From: Postmaster\n");
+
+ s = username(mp->sender);
+ if(s) {
+ s_append(s, " <");
+ s_append(s, s_to_c(mp->sender));
+ s_append(s, ">");
+ } else {
+ s = s_copy(s_to_c(mp->sender));
+ }
+ s = unescapespecial(s);
+ rv = Bprint(fp, "From: %s\n", s_to_c(s));
+ s_free(s);
+ return rv;
+}
+
+static char *
+rewritezone(char *z)
+{
+ int mindiff;
+ char s;
+ Tm *tm;
+ static char x[7];
+
+ tm = localtime(time(0));
+ mindiff = tm->tzoff/60;
+
+ /* if not in my timezone, don't change anything */
+ if(strcmp(tm->zone, z) != 0)
+ return z;
+
+ if(mindiff < 0){
+ s = '-';
+ mindiff = -mindiff;
+ } else
+ s = '+';
+
+ sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
+ return x;
+}
+
+int
+isutf8(String *s)
+{
+ char *p;
+
+ for(p = s_to_c(s); *p; p++)
+ if(*p&0x80)
+ return 1;
+ return 0;
+}
+
+void
+printutf8mime(Biobuf *b)
+{
+ Bprint(b, "MIME-Version: 1.0\n");
+ Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
+ Bprint(b, "Content-Transfer-Encoding: 8bit\n");
+}
+
+/* output a message */
+extern int
+m_print(message *mp, Biobuf *fp, char *remote, int mbox)
+{
+ String *date, *sender;
+ char *f[6];
+ int n;
+
+ sender = unescapespecial(s_clone(mp->sender));
+
+ if (remote != 0){
+ if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
+ s_free(sender);
+ return -1;
+ }
+ } else {
+ if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
+ s_free(sender);
+ return -1;
+ }
+ }
+ s_free(sender);
+ if(!rmail && !mp->havedate){
+ /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
+ date = s_copy(s_to_c(mp->date));
+ n = getfields(s_to_c(date), f, 6, 1, " \t");
+ if(n == 6)
+ Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
+ f[5], f[3], rewritezone(f[4]));
+ }
+ if(!rmail && !mp->havemime && isutf8(mp->body))
+ printutf8mime(fp);
+ if(mp->to){
+ /* add the to: line */
+ if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
+ return -1;
+ /* add the from: line */
+ if (!mp->havefrom && printfrom(mp, fp) < 0)
+ return -1;
+ if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
+ if (Bprint(fp, "\n") < 0)
+ return -1;
+ } else if(!rmail){
+ /* add the from: line */
+ if (!mp->havefrom && printfrom(mp, fp) < 0)
+ return -1;
+ if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
+ if (Bprint(fp, "\n") < 0)
+ return -1;
+ }
+
+ if (!mbox)
+ return m_noescape(mp, fp);
+ return m_escape(mp, fp);
+}
+
+/* print just the message body */
+extern int
+m_bprint(message *mp, Biobuf *fp)
+{
+ return m_noescape(mp, fp);
+}