/* parse a mailbox style header */ int parse_header(char *line, String *sender, String *date) { if (!IS_HEADER(line)) return -1; line += sizeof("From ") - 1; s_restart(sender); while(*line==' '||*line=='\t') line++; if(*line == '"'){ s_putc(sender, *line++); while(*line && *line != '"') s_putc(sender, *line++); s_putc(sender, *line++); } else { while(*line && *line != ' ' && *line != '\t') s_putc(sender, *line++); } s_terminate(sender); s_restart(date); while(*line==' '||*line=='\t') line++; while(*line) s_putc(date, *line++); s_terminate(date); return 0; }
/* Append an input line to a String. * * Returns a pointer to the character string (or 0). * Leading whitespace and newlines are removed. * Lines starting with #include cause us to descend into the new file. * Empty lines and other lines starting with '#' are ignored. */ extern char * s_rdinstack(Sinstack *sp, String *to) { char *p; Biobuf *fp, *nfp; s_terminate(to); fp = sp->fp[sp->depth]; for(;;){ p = rdline(fp, to); if(p == nil){ if(sp->depth == 0) break; Bterm(fp); sp->depth--; return s_rdinstack(sp, to); } if(strncmp(p, "#include", 8) == 0 && (p[8] == ' ' || p[8] == '\t')){ to->ptr = p; p += 8; /* sanity (and looping) */ if(sp->depth >= nelem(sp->fp)) sysfatal("s_recgetline: includes too deep"); /* skip white */ while(*p == ' ' || *p == '\t') p++; nfp = Bopen(p, OREAD); if(nfp == nil) continue; sp->depth++; sp->fp[sp->depth] = nfp; return s_rdinstack(sp, to); } /* got milk? */ if(*p != '#') break; /* take care of comments */ to->ptr = p; s_terminate(to); } return p; }
/* * Get the next token from `line'. The symbol `\l' is replaced by * the name of the local system. */ extern String * rule_parse(String *line, char *system, int *backl) { String *token; String *expanded; char *cp; token = s_parse(line, 0); if(token == 0) return(token); if(strchr(s_to_c(token), '\\')==0) return(token); expanded = s_new(); for(cp = s_to_c(token); *cp; cp++) { if(*cp == '\\') switch(*++cp) { case 'l': s_append(expanded, system); *backl = 1; break; case '\\': s_putc(expanded, '\\'); break; default: s_putc(expanded, '\\'); s_putc(expanded, *cp); break; } else s_putc(expanded, *cp); } s_free(token); s_terminate(expanded); return(expanded); }
/* Get the next field from a String. The field is delimited by white space, * single or double quotes. */ String * s_parse(String *from, String *to) { if (*from->ptr == '\0') return 0; if (to == 0) to = s_new(); if (*from->ptr == '\'') { from->ptr++; for (;*from->ptr != '\'' && *from->ptr != '\0'; from->ptr++) s_putc(to, *from->ptr); if (*from->ptr == '\'') from->ptr++; } else if (*from->ptr == '"') { from->ptr++; for (;*from->ptr != '"' && *from->ptr != '\0'; from->ptr++) s_putc(to, *from->ptr); if (*from->ptr == '"') from->ptr++; } else { for (;!isspace(*from->ptr) && *from->ptr != '\0'; from->ptr++) s_putc(to, *from->ptr); } s_terminate(to); /* crunch trailing white */ while(isspace(*from->ptr)) from->ptr++; return to; }
/* append a char array to a String */ String * s_append(String *to, char *from) { if (to == 0) to = s_new(); if (from == 0) return to; for(; *from; from++) s_putc(to, *from); s_terminate(to); return to; }
/* * Get a line including a crnl into a string. Convert crnl into nl. */ char * getcrnl(String *s) { int c; int count; count = 0; for(;;){ c = Bgetc(&bin); if(debug) Bputc(&berr, c); switch(c){ case -1: s_append(s, "connection closed unexpectedly by remote system"); s_terminate(s); return 0; case '\r': c = Bgetc(&bin); if(c == '\n'){ case '\n': s_putc(s, c); if(debug) Bputc(&berr, c); count++; s_terminate(s); return s->ptr - count; } Bungetc(&bin); s_putc(s, '\r'); if(debug) Bputc(&berr, '\r'); count++; break; default: s_putc(s, c); count++; break; } } }
/* * simplify an address, reduce to a domain */ static String* simplify(char *addr) { int dots, dotlim; char *p, *at; String *s; mklower(addr); at = strchr(addr, '@'); if(at == nil){ /* local address, make it an exact match */ s = s_copy("="); s_append(s, addr); return s; } /* copy up to, and including, the '@' sign */ at++; s = s_copy("~"); for(p = addr; p < at; p++){ if(strchr(".*+?(|)\\[]^$", *p)) s_putc(s, '\\'); s_putc(s, *p); } /* * just any address matching the two most significant domain elements, * except for .uk, which needs three. */ s_append(s, "(.*\\.)?"); p = addr+strlen(addr); /* point at NUL */ if (p[-1] == '.') *--p = '\0'; if (p - addr > 3 && strcmp(".uk", p - 3) == 0) dotlim = 3; else dotlim = 2; dots = 0; while(--p > at) if(*p == '.' && ++dots >= dotlim){ p++; break; } for(; *p; p++){ if(strchr(".*+?(|)\\[]^$", *p) != nil) s_putc(s, '\\'); s_putc(s, *p); } s_terminate(s); return s; }
/* create a new `short' String */ extern String * s_new(void) { String *sp; sp = _s_alloc(); if(sp == nil) sysfatal("s_new: %r"); sp->base = sp->ptr = malloc(STRLEN); if (sp->base == nil) sysfatal("s_new: %r"); sp->end = sp->base + STRLEN; s_terminate(sp); return sp; }
/* * simplify an address, reduce to a domain */ static String* simplify(char *addr) { int dots; char *p, *at; String *s; mklower(addr); at = strchr(addr, '@'); if(at == nil){ /* local address, make it an exact match */ s = s_copy("="); s_append(s, addr); return s; } /* copy up to the '@' sign */ at++; s = s_copy("~"); for(p = addr; p < at; p++){ if(strchr(".*+?(|)\\[]^$", *p)) s_putc(s, '\\'); s_putc(s, *p); } /* just any address matching the two most significant domain elements */ s_append(s, "(.*\\.)?"); p = addr+strlen(addr); dots = 0; for(; p > at; p--){ if(*p != '.') continue; if(dots++ > 0){ p++; break; } } for(; *p; p++){ if(strchr(".*+?(|)\\[]^$", *p) != 0) s_putc(s, '\\'); s_putc(s, *p); } s_terminate(s); return s; }
/* Append an input line to a String. * * Empty lines and leading whitespace are removed. */ static char * rdline(Biobuf *fp, String *to) { int c; int len = 0; c = Bgetc(fp); /* eat leading white */ while(c==' ' || c=='\t' || c=='\n' || c=='\r') c = Bgetc(fp); if(c < 0) return 0; for(;;){ switch(c) { case -1: goto out; case '\\': c = Bgetc(fp); if (c != '\n') { s_putc(to, '\\'); s_putc(to, c); len += 2; } break; case '\r': break; case '\n': if(len != 0) goto out; break; default: s_putc(to, c); len++; break; } c = Bgetc(fp); } out: s_terminate(to); return to->ptr - len; }
/* * get a line that ends in crnl or cr, turn terminating crnl into a nl * * return 0 on EOF */ static int getcrnl(String *s, Biobuf *fp) { int c; for(;;){ c = Bgetc(fp); if(debug) { seek(2, 0, 2); fprint(2, "%c", c); } switch(c){ case 0: break; case -1: goto out; case '\r': c = Bgetc(fp); if(c == '\n'){ if(debug) { seek(2, 0, 2); fprint(2, "%c", c); stamp(); } s_putc(s, '\n'); goto out; } Bungetc(fp); s_putc(s, '\r'); break; case '\n': s_putc(s, c); goto out; default: s_putc(s, c); break; } } out: s_terminate(s); return s_len(s); }
/* create a new `short' String */ extern String * s_newalloc(int len) { String *sp; sp = _s_alloc(); if(sp == nil) sysfatal("s_newalloc: %r"); setmalloctag(sp, getcallerpc(&len)); if(len < STRLEN) len = STRLEN; sp->base = sp->ptr = malloc(len); if (sp->base == nil) sysfatal("s_newalloc: %r"); setmalloctag(sp->base, getcallerpc(&len)); sp->end = sp->base + len; s_terminate(sp); return sp; }
static int rreadstr(char *delim, char *s) /* if s != 0, skip this chars before reading */ { int c; s_reset(sbuf); if(s) skip(s); while((c=GETC()) >= 0){ if(strchr(delim, c)){ UNGETC(c); s_terminate(sbuf); return Tstring; } s_putc(sbuf, c); } if(c == Beof) yyerror("eof in string"); return Terror; }
/* 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; }
static int readname(char *s) { int c; s_reset(sbuf); if(s) skip(s); while((c=GETC()) >= 0 && (isalnum(c) || c == '-')) s_putc(sbuf, c); if(c == Beof){ yyerror("eof in id/class"); return Terror; } UNGETC(c); if(s_len(sbuf) == 0){ yyerror("empty name"); return Terror; } s_terminate(sbuf); return Tstring; }
String * s_dec64(String *sin) { int lin, lout; String *sout; lin = s_len(sin); /* * if the string is coming from smtpd.y, it will have no nl. * if it is coming from getcrnl below, it will have an nl. */ if (*(s_to_c(sin)+lin-1) == '\n') lin--; sout = s_newalloc(lin+1); lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin); if (lout < 0) { s_free(sout); return nil; } sout->ptr = sout->base + lout; s_terminate(sout); return sout; }
int yylex(void) { int c, r; char c1; Sym *s; genblock: if(block != nblock){ if(block > nblock){ if(debug) fprint(2, "%s %d -> %d\n", ty(Tend), block, nblock); block--; return Tend; }else{ if(debug) fprint(2, "%s %d -> %d\n", ty(Tbegin), block, nblock); block++; return Tbegin; } } c = GETC(); if(c == Beof){ nblock = 0; if(block != nblock) goto genblock; return 0; } UNGETC(c); switch(state){ case Head0: /* [%type] string */ c = GETC(); if(c != '%'){ UNGETC(c); setstate(Body0); goto genblock; } r = readname(" \t"); if(r == Terror) return fail(Head2); s = lookup(s_to_c(sbuf)); yylval.sym = s; if(debug) fprint(2, "Sym[%s] %s\n", ty(s->type), s->name); setstate(Head1); return s->type; case Head1: /* %type [string] */ r = readstr("\n", " \t", 0); if(r == Terror) return fail(Head2); yylval.s = estrdup(s_to_c(sbuf)); if(debug) fprint(2, "%s %q\n", ty(Tstring), yylval.s); setstate(Head2); return Tstring; case Head2: skip(" \t"); c = GETC(); assert(c == '\n' || c == Beof); setstate(Head0); return ';'; case Body0: /* [indent] cmd inline */ c = GETC(); if(c == '%'){ UNGETC(c); setstate(Head0); goto genblock; } UNGETC(c); r = skip(" \t"); c = GETC(); if(c == '\n'){ if(debug) fprint(2, "%s\n", ty(Tbreak)); return Tbreak; } UNGETC(c); nblock = r; setstate(Body1); goto genblock; case Body1: /* indent [cmd] inline */ switch(c = GETC()){ case Beof: yyerror("eof in body"); if(debug) fprint(2, "%s\n", ty(Terror)); return fail(Body3); case '=': case '\\': case '+': case '*': case ':': case '-': case '>': if(debug) fprint(2, "%c\n", c); setstate(Body2); return c; case '!': if(debug) fprint(2, "%c\n", c); setstate(Code); return c; case '{': case '}': if(debug) fprint(2, "%c\n", c); setstate(Body3); return c; case '#': if(debug) fprint(2, "%c\n", c); setstate(ID); return c; case '.': if(debug) fprint(2, "%c\n", c); setstate(Class); return c; case '|': if(debug) fprint(2, "%c\n", c); setstate(Table); return c; default: UNGETC(c); if(debug) fprint(2, "\\\n"); setstate(Body2); return '\\'; } case Body2: /* indent cmd [inline] */ case Table: r = skip(" \t"); if(state == Table && r > 0) return ','; switch(c = getcc(&c1, "*[]|<>")){ case Beof: yyerror("eof in body"); if(debug) fprint(2, "%s\n", ty(Terror)); return fail(Body3); case '\n': UNGETC(c); setstate(Body3); break; case '*': case '[': case ']': case '|': case '<': case '>': if(c == '[' || c == '|' || c == '<') skip(" \t\n"); if(debug) fprint(2, "%c\n", c); return c; default: s_reset(sbuf); s_putc(sbuf, c1); while((c=getcc(&c1, "*[]|<>")) == 0){ if(state == Table && c1 == '\t'){ UNGETC(c); break; } s_putc(sbuf, c1); } s_terminate(sbuf); if(c > 0) UNGETC(c); yylval.s = estrdup(s_to_c(sbuf)); if(debug) fprint(2, "%s %q\n", ty(Tstring), yylval.s); return Tstring; } case Body3: skip(" \t"); c = GETC(); if(c != '\n') return fail(Body3); setstate(Body0); return ';'; case Code: r = readstr("\n", "", 1); if(r == Terror){ if(debug) fprint(2, "%s\n", ty(Terror)); return fail(Body3); } setstate(Body3); yylval.s = estrdup(s_to_c(sbuf)); if(debug) fprint(2, "%s %q\n", ty(Tstring), yylval.s); return Tstring; case ID: case Class: r = readname(" \t"); if(r == Terror){ if(debug) fprint(2, "%s\n", ty(Terror)); return fail(Body3); } setstate(Body3); yylval.s = estrdup(s_to_c(sbuf)); if(debug) fprint(2, "%s %q\n", ty(Tstring), yylval.s); return Tstring; default: assert(0); return Terror; } }
void sender(String *path) { String *s; static char *lastsender; if(rejectcheck()) return; if (authenticate && !authenticated) { rejectcount++; reply("530 5.7.0 Authentication required\r\n"); return; } if(him == 0 || *him == 0){ rejectcount++; reply("503 Start by saying HELO, please.\r\n", s_to_c(path)); return; } /* don't add the domain onto black holes or we will loop */ if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){ s = s_new(); s_append(s, him); s_append(s, "!"); s_append(s, s_to_c(path)); s_terminate(s); s_free(path); path = s; } if(shellchars(s_to_c(path))){ rejectcount++; reply("501 5.1.3 Bad character in sender address %s.\r\n", s_to_c(path)); return; } /* * if the last sender address resulted in a rejection because the sending * domain didn't exist and this sender has the same domain, reject * immediately. */ if(lastsender){ if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){ filterstate = REFUSED; rejectcount++; reply("554 5.1.8 Sender domain must exist: %s\r\n", s_to_c(path)); return; } free(lastsender); /* different sender domain */ lastsender = 0; } /* * see if this ip address, domain name, user name or account is blocked */ logged = 0; filterstate = blocked(path); /* * permanently reject what we can before trying smtp ping, which * often leads to merely temporary rejections. */ switch (filterstate){ case DENIED: syslog(0, "smtpd", "Denied %s (%s/%s)", s_to_c(path), him, nci->rsys); rejectcount++; logged++; reply("554-5.7.1 We don't accept mail from %s.\r\n", s_to_c(path)); reply("554 5.7.1 Contact postmaster@%s for more information.\r\n", dom); return; case REFUSED: syslog(0, "smtpd", "Refused %s (%s/%s)", s_to_c(path), him, nci->rsys); rejectcount++; logged++; reply("554 5.7.1 Sender domain must exist: %s\r\n", s_to_c(path)); return; } listadd(&senders, path); reply("250 2.0.0 sender is %s\r\n", s_to_c(path)); }
static String * substitute(String *source, Resub *subexp, message *mp) { int i; char *s; char *sp; String *stp; if(source == 0) return 0; sp = s_to_c(source); /* someplace to put it */ stp = s_new(); /* do the substitution */ while (*sp != '\0') { if(*sp == '\\') { switch (*++sp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = *sp-'0'; if(subexp[i].s.sp != 0) for (s = subexp[i].s.sp; s < subexp[i].e.ep; s++) s_putc(stp, *s); break; case '\\': s_putc(stp, '\\'); break; case '\0': sp--; break; case 's': for(s = s_to_c(mp->replyaddr); *s; s++) s_putc(stp, *s); break; case 'p': if(mp->bulk) s = "bulk"; else s = "normal"; for(;*s; s++) s_putc(stp, *s); break; default: s_putc(stp, *sp); break; } } else if(*sp == '&') { if(subexp[0].s.sp != 0) for (s = subexp[0].s.sp; s < subexp[0].e.ep; s++) s_putc(stp, *s); } else if(*sp == '$') { sp = getrcvar(sp+1, &s); s_append(stp, s); free(s); sp--; /* counter sp++ below */ } else s_putc(stp, *sp); sp++; } s_terminate(stp); return s_restart(stp); }
/* 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; }