int acl_read2(maildir_aclt_list *l, struct maildir_info *minfo, char **owner) { int rc; char *p; if (minfo->mailbox_type == MAILBOXTYPE_OLDSHARED) { /* Legacy shared., punt. */ maildir_aclt_list_init(l); if (maildir_aclt_list_add(l, "anyone", ACL_LOOKUP ACL_READ ACL_SEEN ACL_WRITE ACL_INSERT ACL_DELETEMSGS ACL_EXPUNGE, NULL) < 0 || (*owner=strdup("vendor=courier.internal")) == NULL) { maildir_aclt_list_destroy(l); return -1; } return 0; } if (minfo->homedir == NULL || minfo->maildir == NULL) return -1; p=maildir_name2dir(".", minfo->maildir); if (!p) return -1; rc=maildir_acl_read(l, minfo->homedir, strncmp(p, "./", 2) == 0 ? p+2:p); free(p); if (owner && rc == 0) { *owner=minfo->owner; minfo->owner=NULL; } return rc; }
static char *smap_path(const char *homedir, char **words) /* words[0] better be INBOX! */ { struct get_existing_folder_info gefi; char *n, *p; struct stat stat_buf; if ((n=smaptoUtf7(words)) == NULL) return NULL; p=maildir_name2dir(homedir, n); if (!p) { free(n); return NULL; } if (stat(p, &stat_buf) == 0) { free(p); return n; } gefi.fn=words; gefi.pathname=NULL; maildir_list(homedir ? homedir:".", &get_existing_callback, &gefi); if (gefi.pathname) { free(n); free(p); return gefi.pathname; } free(p); return n; }
static void doupdate() { maildir_aclt_list l; char *owner; char buf[2]; char *p; struct maildir_info minfo; if (maildir_info_imap_find(&minfo, sqwebmail_folder, login_returnaddr()) < 0) return; if (acl_read2(&l, &minfo, &owner) < 0) { maildir_info_destroy(&minfo); return; } strcpy(buf, ACL_ADMINISTER); acl_computeRights(&l, buf, owner); if (!*buf) { if (owner) free(owner); maildir_aclt_list_destroy(&l); maildir_info_destroy(&minfo); return; } if (*cgi("delentity")) { if (maildir_aclt_list_del(&l, cgi("delentity"))) printf("%s", getarg("ACL_failed")); } if (*cgi("do.update")) { char *entity=NULL; const char *p; char new_acl[40]; p=cgi("entitytype"); if (strcmp(p, "anonymous") == 0 || strcmp(p, "owner") == 0) entity=strdup(p); else if (strcmp(p, "user") == 0) { p=cgi("entity"); if (*p) { entity=malloc(sizeof("user="******"user="******"group") == 0) { p=cgi("entity"); if (*p) { entity=malloc(sizeof("group=")+strlen(p)); if (entity) strcat(strcpy(entity, "group="), p); } } else { entity=strdup(cgi("entity")); } if (*cgi("negate") == '-' && entity) { char *p=malloc(strlen(entity)+2); if (p) strcat(strcpy(p, "-"), entity); free(entity); entity=p; } if (entity) { char *val= unicode_convert_toutf8(entity, sqwebmail_content_charset, NULL); if (val) { free(entity); entity=val; } } p=getarg("ACL_all"); new_acl[0]=0; while (*p && strlen(new_acl) < sizeof(new_acl)-2) { char b[40]; sprintf(b, "acl_%c", *p); if (*cgi(b)) { b[0]=*p; b[1]=0; strcat(new_acl, b); } ++p; } if (!entity || !*entity || maildir_aclt_list_add(&l, entity, new_acl, NULL) < 0) printf("%s", getarg("ACL_failed")); if (entity) free(entity); } p=maildir_name2dir(".", minfo.maildir); if (p) { const char *err_ident; if (maildir_acl_write(&l, minfo.homedir, strncmp(p, "./", 2) == 0 ? p+2:p, owner, &err_ident)) printf("%s", getarg("ACL_failed")); free(p); } if (owner) free(owner); maildir_aclt_list_destroy(&l); maildir_info_destroy(&minfo); }
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); }
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); }