int maildir_filter_ruleupdate(struct maildirfilter *r, struct maildirfilterrule *p, const char *name, enum maildirfiltertype type, int flags, const char *header, const char *value, const char *folder, const char *fromhdr, int *errcode) { const char *c; /* ** Before creating a new rule, validate all input. */ *errcode=0; /* rule name: may not contain quotes or control characters. */ *errcode=MF_ERR_BADRULENAME; if (!name || !*name || strlen(name) > 200) return (-1); for (c=name; *c; c++) if ((unsigned char)*c < ' ' || *c == '\'' || *c == '"' || *c == '`') return (-1); /* rule type: we must know what it is */ switch (type) { case startswith: case endswith: case contains: case hasrecipient: case mimemultipart: case textplain: case islargerthan: break; default: *errcode=MF_ERR_BADRULETYPE; break; } ; /* header: */ *errcode=MF_ERR_BADRULEHEADER; c=header; if (c && strlen(c) > 200) return (-1); if (c == 0 || *c == 0) { switch (type) { case hasrecipient: case islargerthan: case mimemultipart: case textplain: break; case contains: case startswith: case endswith: if (flags & MFR_BODY) break; /* FALLTHRU */ default: /* required */ return (-1); } } else for ( ; *c; c++) { /* no control characters */ if (*c <= ' ' || *c == MDIRSEP[0] || *c >= 127 || *c == '\'' || *c == '\\' || *c == '"' || *c == '`' || *c == '/') return (-1); } /* rule pattern */ *errcode=MF_ERR_BADRULEVALUE; c=value; if (c && strlen(c) > 200) return (-1); if (c == 0 || *c == 0) { switch (type) { case mimemultipart: case textplain: break; default: /* required */ return (-1); } } else if (!(flags & MFR_PLAINSTRING)) { /* ** This is ugly. We: make sure parenthesis match up, and look out for ** special characters. */ int lparencount=0; while (*c) { if (*c == '/' || *c == '$' || *c == '!' || *c == '`' || (int)(unsigned char)*c < ' ' || *c == '\'' || *c == '"') return (-1); /* must be escaped */ if (type == islargerthan) { if (!isdigit((int)(unsigned char)*c)) return (-1); } if (*c == '(') { if (type == hasrecipient) return (-1); ++lparencount; ++c; if (*c == ')') return (-1); continue; } if (*c == ')') { if (type == hasrecipient) return (-1); if (lparencount == 0) return (-1); ++c; --lparencount; continue; } if (*c == '[') /* This is a set */ { if (type == hasrecipient) return (-1); ++c; for (;;) { if (*c == '\'' || *c == '"' || *c == '`') return (-1); /* must be quoted*/ if (*c == '\\') ++c; if (!*c) return (-1); if ((int)(unsigned char)*c < ' ') return (-1); ++c; if (*c == ']') break; if (*c != '-') continue; ++c; if (*c == '\'' || *c == '"' || *c == '`') return (-1); /* must be quoted*/ if (*c == '\\') ++c; if ((int)(unsigned char)*c < ' ') return (-1); if (!*c) return (-1); ++c; } ++c; continue; } if (*c == '\\') { if (type == hasrecipient) return (-1); ++c; } if (!*c) return (-1); ++c; } if (lparencount) return (-1); /* not balanced */ } /* validate FROM header */ *errcode=MF_ERR_BADFROMHDR; while (fromhdr && *fromhdr && isspace((int)(unsigned char)*fromhdr)) ++fromhdr; for (c=fromhdr; *c; c++) if (*c == '\'' || *c == '\\' || (int)(unsigned char)*c < ' ') return (-1); *errcode=MF_ERR_BADRULEFOLDER; /* validate name of destination folder */ c=folder; if (!c) return (-1); if (strlen(c) > 200) return (-1); if (*c == '*' || *c == '!') { /* Forward, or bounce with an error */ ++c; for ( ; *c; c++) { if (strchr("'\"$\\`;(){}#&<>~", *c) || (unsigned char)*c < ' ') return (-1); } } else if (*c == '+') /* Autorespond */ { struct maildir_filter_autoresp_info ai; if (maildir_filter_autoresp_info_init_str(&ai, c+1)) return (-1); maildir_filter_autoresp_info_free(&ai); } else if (strcmp(c, ".")) { for ( ; *c; c++) { if (*c == '.' && (c[1] == '.' || c[1] == 0)) return (-1); if ((int)(unsigned char)*c < ' ' || *c == '\\' || *c == '|' || *c == '!') return (-1); } } /* OK, we're good */ *errcode=MF_ERR_INTERNAL; if (p->rulename) free(p->rulename); if ((p->rulename=strdup(name)) == 0) return (-1); p->type=type; if (p->fieldname) free(p->fieldname); if ((p->fieldname=strdup(header ? header:"")) == 0) return (-1); if (p->fieldvalue) free(p->fieldvalue); if ((p->fieldvalue=strdup(value ? value:"")) == 0) return (-1); if (p->tofolder) free(p->tofolder); if ((p->tofolder=malloc(strlen(folder)+2)) == 0) return (-1); *p->tofolder=0; /* Prefix with dot by default */ if (*folder != '*' && *folder != '!' && *folder != '.' && *folder != '+') strcpy(p->tofolder, "."); strcat(p->tofolder, folder); if (p->fromhdr) free(p->fromhdr); if ((p->fromhdr=strdup(fromhdr ? fromhdr:"")) == NULL) return (-1); p->flags=flags; return (0); }
int maildir_filter_saverules(struct maildirfilter *r, const char *filename, const char *maildir, const char *maildirpath, const char *fromaddr) { FILE *f=fopen(filename, "w"); struct maildirfilterrule *p; if (!f) return (-1); fprintf(f, "#MFMAILDROP=2\n" "#\n" "# DO NOT EDIT THIS FILE. This is an automatically" " generated filter.\n" "\n"); for (fprintf(f, "FROM='"); *fromaddr; fromaddr++) { if (*fromaddr == '\'' || *fromaddr == '\\') putc('\\', f); putc(*fromaddr, f); } fprintf(f, "\'\n"); for (p=r->first; p; p=p->next) { const char *fieldname=p->fieldname_utf8 ? p->fieldname_utf8:""; const char *fieldvalue=p->fieldvalue_utf8 ? p->fieldvalue_utf8:""; const char *tofolder=p->tofolder ? p->tofolder:""; fprintf(f, "##Op:%s\n", typelist[p->type].name); fprintf(f, "##Header:%s\n", fieldname); fprintf(f, "##Value:%s\n", fieldvalue); fprintf(f, "##Folder:%s\n", strcmp(tofolder, INBOX) == 0 ? ".": strncmp(tofolder, INBOX ".", sizeof(INBOX)) == 0 ? strchr(tofolder, '.'):tofolder); fprintf(f, "##From:%s\n", p->fromhdr ? p->fromhdr:""); if (p->flags & MFR_PLAINSTRING) fprintf(f, "##PlainString\n"); if (p->flags & MFR_DOESNOT) fprintf(f, "##DoesNot\n"); if (p->flags & MFR_BODY) fprintf(f, "##Body\n"); if (p->flags & MFR_CONTINUE) fprintf(f, "##Continue\n"); fprintf(f, "##Name:%s\n\n", p->rulename_utf8 ? p->rulename_utf8:""); fprintf(f, "\nif ("); if (p->flags & MFR_DOESNOT) fprintf(f, "!"); fprintf(f, "("); switch (p->type) { case startswith: if (p->flags & MFR_BODY) { fprintf(f, "/^"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/:b"); } else { fprintf(f, "/^%s: *", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/"); } break; case endswith: if (p->flags & MFR_BODY) { fprintf(f, "/"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "$/:b"); } else { fprintf(f, "/^%s:.*", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "$/"); } break; case contains: if (p->flags & MFR_BODY) { fprintf(f, "/"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/:b"); } else { fprintf(f, "/^%s:.*", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/"); } break; case hasrecipient: fprintf(f, "hasaddr(\"%s\")", fieldvalue); break; case mimemultipart: fprintf(f, "/^Content-Type: *multipart\\/mixed/"); break; case textplain: fprintf(f, " (! /^Content-Type:/) || " "/^Content-Type: text\\/plain$/ || " "/^Content-Type: text\\/plain;/"); break; case islargerthan: fprintf(f, "$SIZE > %s", fieldvalue); break; case anymessage: fprintf(f, "1"); break; } fprintf(f, "))\n" "{\n"); if (*tofolder == '!') { fprintf(f, " %s \"| $SENDMAIL -f \" '\"\"' \" %s\"\n", p->flags & MFR_CONTINUE ? "cc":"to", tofolder+1); } else if (*tofolder == '*') { fprintf(f, " echo \"%s\"\n" " EXITCODE=%d\n" " exit\n", tofolder+1, EX_SOFTWARE); } else if (*tofolder == '+') { struct maildir_filter_autoresp_info ai; if (maildir_filter_autoresp_info_init_str(&ai, tofolder+1) == 0) { if (p->fromhdr && p->fromhdr[0]) { const char *cp; fprintf(f, " AUTOREPLYFROM='"); for (cp=p->fromhdr; *cp; ++cp) { if (*cp == '\'' || *cp == '\\') putc('\\', f); putc(*cp, f); } fprintf(f, "'\n"); } else fprintf(f, " AUTOREPLYFROM=\"$FROM\"\n" ); fprintf(f, " `%s -A \"X-Sender: $FROM\"" " -A \"From: $AUTOREPLYFROM\"", MAILBOT); if (ai.dsnflag) fprintf(f, " -M \"$FROM\""); fprintf(f, " -m \"%s/autoresponses/%s\"", maildirpath, ai.name); if (ai.noquote) fprintf(f, " -N"); if (ai.days > 0) fprintf(f, " -d \"%s/autoresponses/" "%s.dat\" -D %u", maildirpath, ai.name, ai.days); fprintf(f, " $SENDMAIL -t -f \"\"`\n"); maildir_filter_autoresp_info_free(&ai); } } else if (strcmp(tofolder, "exit") == 0) { fprintf(f, " exit\n"); } else { char *s; s=maildir_name2dir(maildirpath, tofolder); if (!s) fprintf(f, " # INTERNAL ERROR in maildir_name2dir\n"); else { fprintf(f, " %s \"%s/.\"\n", p->flags & MFR_CONTINUE ? "cc":"to", s); free(s); } } fprintf(f, "}\n\n"); } fflush(f); if (ferror(f)) { fclose(f); return (-1); } fprintf(f, "to \"%s/.\"\n", maildirpath); if (fclose(f)) return (-1); if (chmod(filename, 0600)) return (-1); return (0); }
int maildir_filter_saverules(struct maildirfilter *r, const char *filename, const char *maildir, const char *maildirpath, const char *fromaddr) { FILE *f=fopen(filename, "w"); struct maildirfilterrule *p; if (!f) return (-1); fprintf(f, "#MFMAILDROP=2\n" "#\n" "# DO NOT EDIT THIS FILE. This is an automatically" " generated filter.\n" "\n"); for (fprintf(f, "FROM='"); *fromaddr; fromaddr++) { if (*fromaddr != '"' && *fromaddr != '\'' && *fromaddr != '\\') putc(*fromaddr, f); } fprintf(f, "\'\nimport SENDER\nif ($SENDER eq \"\")\n{\n SENDER=$FROM\n}\n\n"); for (p=r->first; p; p=p->next) { const char *fieldname=p->fieldname ? p->fieldname:""; const char *fieldvalue=p->fieldvalue ? p->fieldvalue:""; const char *tofolder=p->tofolder ? p->tofolder:""; fprintf(f, "##Op:%s\n", typelist[p->type].name); fprintf(f, "##Header:%s\n", fieldname); fprintf(f, "##Value:%s\n", fieldvalue); fprintf(f, "##Folder:%s\n", tofolder); fprintf(f, "##From:%s\n", p->fromhdr ? p->fromhdr:""); if (p->flags & MFR_PLAINSTRING) fprintf(f, "##PlainString\n"); if (p->flags & MFR_DOESNOT) fprintf(f, "##DoesNot\n"); if (p->flags & MFR_BODY) fprintf(f, "##Body\n"); if (p->flags & MFR_CONTINUE) fprintf(f, "##Continue\n"); fprintf(f, "##Name:%s\n\n", p->rulename ? p->rulename:""); fprintf(f, "\nif ("); if (p->flags & MFR_DOESNOT) fprintf(f, "!"); fprintf(f, "("); switch (p->type) { case startswith: if (p->flags & MFR_BODY) { fprintf(f, "/^"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/:b"); } else { fprintf(f, "/^%s: *", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/"); } break; case endswith: if (p->flags & MFR_BODY) { fprintf(f, "/"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "$/:b"); } else { fprintf(f, "/^%s:.*", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "$/"); } break; case contains: if (p->flags & MFR_BODY) { fprintf(f, "/"); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/:b"); } else { fprintf(f, "/^%s:.*", fieldname); print_pattern(f, p->flags, fieldvalue); fprintf(f, "/"); } break; case hasrecipient: fprintf(f, "hasaddr(\"%s\")", fieldvalue); break; case mimemultipart: fprintf(f, "/^Content-Type: *multipart\\/mixed/"); break; case textplain: fprintf(f, " (! /^Content-Type:/) || " "/^Content-Type: text\\/plain$/ || " "/^Content-Type: text\\/plain;/"); break; case islargerthan: fprintf(f, "$SIZE > %s", fieldvalue); break; } fprintf(f, "))\n" "{\n"); if (*tofolder == '!') { fprintf(f, " %s \"| $SENDMAIL -f \" '\"$SENDER\"' \" %s\"\n", p->flags & MFR_CONTINUE ? "cc":"to", tofolder+1); } else if (*tofolder == '*') { fprintf(f, " echo \"%s\"\n" " EXITCODE=77\n" " exit\n", tofolder+1); } else if (*tofolder == '+') { struct maildir_filter_autoresp_info ai; if (maildir_filter_autoresp_info_init_str(&ai, tofolder+1) == 0) { if (p->fromhdr && p->fromhdr[0]) fprintf(f, " AUTOREPLYFROM='%s'\n", p->fromhdr); else fprintf(f, " AUTOREPLYFROM=$FROM\n" ); fprintf(f, " `%s -A \"X-Sender: $FROM\"" " -A \"From: $AUTOREPLYFROM\"", MAILBOT); if (ai.dsnflag) fprintf(f, " -M \"$FROM\""); fprintf(f, " -m \"%s/autoresponses/%s\"", maildirpath, ai.name); if (ai.days > 0) fprintf(f, " -d \"%s/autoresponses/" "%s.dat\" -D %u", maildirpath, ai.name, ai.days); fprintf(f, " $SENDMAIL -t -f \"\"`\n"); maildir_filter_autoresp_info_free(&ai); } } else { if (*tofolder == '.') ++tofolder; /* Strip leading dot */ fprintf(f, " %s \"%s%s%s/.\"\n", p->flags & MFR_CONTINUE ? "cc":"to", maildirpath, *tofolder ? "/.":"", tofolder); } fprintf(f, "}\n\n"); } fflush(f); if (ferror(f)) { fclose(f); return (-1); } fprintf(f, "to \"%s/.\"\n", maildirpath); if (fclose(f)) return (-1); if (chmod(filename, 0600)) return (-1); return (0); }
static int maildir_filter_ruleupdate_utf8(struct maildirfilter *r, struct maildirfilterrule *p, const char *name, enum maildirfiltertype type, int flags, const char *header, const char *value, const char *folder, const char *fromhdr, int *errcode) { const char *c; struct maildirfilterrule *pom; /* ** Before creating a new rule, validate all input. */ *errcode=0; /* rule name: may not contain quotes or control characters. */ *errcode=MF_ERR_BADRULENAME; if (!*name || strlen(name) > 200) return (-1); for (c=name; *c; c++) if ((unsigned char)*c < ' ' || *c == '\'' || *c == '"' || *c == '`') return (-1); /* rule name: may not already exist */ *errcode=MF_ERR_EXISTS; for (pom=r->first; pom->next; pom=pom->next) { if (p!=pom && !strcmp(name, pom->rulename_utf8)) return (-1); } /* rule type: we must know what it is */ switch (type) { case startswith: case endswith: case contains: case hasrecipient: case mimemultipart: case textplain: case islargerthan: case anymessage: break; default: *errcode=MF_ERR_BADRULETYPE; break; } ; /* header: */ *errcode=MF_ERR_BADRULEHEADER; c=header; if (strlen(c) > 200) return (-1); if (*c == 0) { switch (type) { case hasrecipient: case islargerthan: case mimemultipart: case textplain: case anymessage: break; case contains: case startswith: case endswith: if (flags & MFR_BODY) break; /* FALLTHRU */ default: /* required */ return (-1); } } else for ( ; *c; c++) { /* no control characters */ if ((unsigned char)*c <= ' ' || *c == MDIRSEP[0] || *c == '\'' || *c == '\\' || *c == '"' || *c == '`' || *c == '/') return (-1); } /* rule pattern */ *errcode=MF_ERR_BADRULEVALUE; c=value; if (strlen(c) > 200) return (-1); if (*c == 0) { switch (type) { case mimemultipart: case textplain: case anymessage: break; default: /* required */ return (-1); } } else if (!(flags & MFR_PLAINSTRING)) { /* ** Let PCRE decide if this is a valid pattern. ** ** One exception: the forward slash character, and some other ** special characters, must always be escaped. */ while (*c) { if (*c == '/' || *c == '$' || *c == '!' || *c == '`' || (int)(unsigned char)*c < ' ' || *c == '\'' || *c == '"') return (-1); /* must be escaped */ if (type == islargerthan) { if (!isdigit((int)(unsigned char)*c)) return (-1); } if (*c == '(') { if (type == hasrecipient) return (-1); ++c; if (*c == ')') return (-1); continue; } if (*c == ')') { if (type == hasrecipient) return (-1); ++c; continue; } if (*c == '[') /* This is a set */ { if (type == hasrecipient) return (-1); ++c; for (;;) { if (*c == '\'' || *c == '"' || *c == '`') return (-1); /* must be quoted*/ if (*c == '\\') ++c; if (!*c) return (-1); if ((int)(unsigned char)*c < ' ') return (-1); ++c; if (*c == ']') break; if (*c != '-') continue; ++c; if (*c == '\'' || *c == '"' || *c == '`') return (-1); /* must be quoted*/ if (*c == '\\') ++c; if ((int)(unsigned char)*c < ' ') return (-1); if (!*c) return (-1); ++c; if (*c == ']') break; } ++c; continue; } if (*c == '\\') { if (type == hasrecipient) return (-1); ++c; } if (!*c) return (-1); ++c; } #if HAVE_PCRE_H switch (type) { case contains: case startswith: case endswith: { const char *errptr; int errindex; pcre *p=pcre_compile(value, PCRE_UTF8, &errptr, &errindex, 0); if (p == NULL) return -1; pcre_free(p); } break; default: break; } #endif } /* validate FROM header */ *errcode=MF_ERR_BADFROMHDR; while (fromhdr && *fromhdr && isspace((int)(unsigned char)*fromhdr)) ++fromhdr; for (c=fromhdr; *c; c++) if ((int)(unsigned char)*c < ' ') return (-1); *errcode=MF_ERR_BADRULEFOLDER; /* validate name of destination folder */ c=folder; if (!c) return (-1); if (strlen(c) > 200) return (-1); if (*c == '*' || *c == '!') { /* Forward, or bounce with an error */ ++c; for ( ; *c; c++) { if (strchr("'\"$\\`;(){}#&<>~", *c) || (unsigned char)*c < ' ') return (-1); } } else if (*c == '+') /* Autorespond */ { struct maildir_filter_autoresp_info ai; if (maildir_filter_autoresp_info_init_str(&ai, c+1)) return (-1); maildir_filter_autoresp_info_free(&ai); } else if (strcmp(c, "exit") == 0) /* Purge */ { } else { char *s; if (strcmp(c, INBOX) && strncmp(c, INBOX ".", sizeof(INBOX))) return -1; s=maildir_name2dir(".", c); if (!s) return -1; free(s); } /* OK, we're good */ *errcode=MF_ERR_INTERNAL; if (p->rulename_utf8) free(p->rulename_utf8); if ((p->rulename_utf8=strdup(name)) == 0) return (-1); p->type=type; if (p->fieldname_utf8) free(p->fieldname_utf8); if ((p->fieldname_utf8=strdup(header ? header:"")) == 0) return (-1); if (p->fieldvalue_utf8) free(p->fieldvalue_utf8); if ((p->fieldvalue_utf8=strdup(value ? value:"")) == 0) return (-1); if (p->tofolder) free(p->tofolder); if ((p->tofolder=malloc(strlen(folder)+1)) == 0) return (-1); strcpy(p->tofolder, folder); if (p->fromhdr) free(p->fromhdr); if ((p->fromhdr=strdup(fromhdr ? fromhdr:"")) == NULL) return (-1); p->flags=flags; return (0); }