void mail_free(struct mail *m) { if (m->attach != NULL) attach_free(m->attach); if (m->tags != NULL) strb_destroy(&m->tags); ARRAY_FREE(&m->wrapped); m->wrapchar = '\0'; if (m->auxfree != NULL && m->auxdata != NULL) m->auxfree(m->auxdata); }
/* Check mail against next rule or part of expression. */ int mail_match(struct mail_ctx *mctx, struct msg *msg, struct msgbuf *msgbuf) { struct account *a = mctx->account; struct mail *m = mctx->mail; struct expritem *ei; struct replstrs *users; int should_free, this = -1, error = MAIL_CONTINUE; char desc[DESCBUFSIZE]; set_wrapped(m, ' '); /* If blocked, check for msgs from parent. */ if (mctx->msgid != 0) { if (msg == NULL || msg->id != mctx->msgid) return (MAIL_BLOCKED); mctx->msgid = 0; if (msg->type != MSG_DONE) fatalx("unexpected message"); if (msgbuf->buf != NULL && msgbuf->len != 0) { strb_destroy(&m->tags); m->tags = msgbuf->buf; reset_tags(&m->tags); } ei = mctx->expritem; switch (msg->data.error) { case MATCH_ERROR: return (MAIL_ERROR); case MATCH_TRUE: this = 1; break; case MATCH_FALSE: this = 0; break; default: fatalx("unexpected response"); } apply_result(ei, &mctx->result, this); goto next_expritem; } /* Check for completion and end of ruleset. */ if (mctx->done) return (MAIL_DONE); if (mctx->rule == NULL) { switch (conf.impl_act) { case DECISION_NONE: log_warnx("%s: reached end of ruleset. no " "unmatched-mail option; keeping mail", a->name); m->decision = DECISION_KEEP; break; case DECISION_KEEP: log_debug2("%s: reached end of ruleset. keeping mail", a->name); m->decision = DECISION_KEEP; break; case DECISION_DROP: log_debug2("%s: reached end of ruleset. dropping mail", a->name); m->decision = DECISION_DROP; break; } return (MAIL_DONE); } /* Expression not started. Start it. */ if (mctx->expritem == NULL) { /* Start the expression. */ mctx->result = 0; mctx->expritem = TAILQ_FIRST(mctx->rule->expr); } /* Check this expression item and adjust the result. */ ei = mctx->expritem; /* Handle short-circuit evaluation. */ switch (ei->op) { case OP_NONE: break; case OP_AND: /* And and the result is already false. */ if (!mctx->result) goto next_expritem; break; case OP_OR: /* Or and the result is already true. */ if (mctx->result) goto next_expritem; break; } switch (ei->match->match(mctx, ei)) { case MATCH_ERROR: return (MAIL_ERROR); case MATCH_PARENT: return (MAIL_BLOCKED); case MATCH_TRUE: this = 1; break; case MATCH_FALSE: this = 0; break; default: fatalx("unexpected op"); } apply_result(ei, &mctx->result, this); ei->match->desc(ei, desc, sizeof desc); log_debug3("%s: tried %s, result now %d", a->name, desc, mctx->result); next_expritem: /* Move to the next item. If there is one, then return. */ mctx->expritem = TAILQ_NEXT(mctx->expritem, entry); if (mctx->expritem != NULL) return (MAIL_CONTINUE); log_debug3("%s: finished rule %u, result %d", a->name, mctx->rule->idx, mctx->result); /* If the result was false, skip to find the next rule. */ if (!mctx->result) goto next_rule; log_debug2("%s: matched to rule %u", a->name, mctx->rule->idx); /* * If this rule is stop, mark the context so when we get back after * delivery we know to stop. */ if (mctx->rule->stop) mctx->done = 1; /* Handle nested rules. */ if (!TAILQ_EMPTY(&mctx->rule->rules)) { log_debug2("%s: entering nested rules", a->name); /* * Stack the current rule (we are at the end of it so the * the expritem must be NULL already). */ ARRAY_ADD(&mctx->stack, mctx->rule); /* Continue with the first rule of the nested list. */ mctx->rule = TAILQ_FIRST(&mctx->rule->rules); return (MAIL_CONTINUE); } mctx->matched = 1; /* Handle lambda actions. */ if (mctx->rule->lambda != NULL) { users = find_delivery_users(mctx, NULL, &should_free); chained = MAXACTIONCHAIN; if (fill_from_action(mctx, mctx->rule, mctx->rule->lambda, users) != 0) { if (should_free) ARRAY_FREEALL(users); return (MAIL_ERROR); } if (should_free) ARRAY_FREEALL(users); error = MAIL_DELIVER; } /* Fill the delivery action queue. */ if (!ARRAY_EMPTY(mctx->rule->actions)) { chained = MAXACTIONCHAIN; if (fill_from_strings(mctx, mctx->rule, mctx->rule->actions) != 0) return (MAIL_ERROR); error = MAIL_DELIVER; } next_rule: /* Move to the next rule. */ mctx->ruleidx = mctx->rule->idx; /* save last index */ mctx->rule = TAILQ_NEXT(mctx->rule, entry); /* If no more rules, try to move up the stack. */ while (mctx->rule == NULL) { if (ARRAY_EMPTY(&mctx->stack)) break; log_debug2("%s: exiting nested rules", a->name); mctx->rule = ARRAY_LAST(&mctx->stack); mctx->rule = TAILQ_NEXT(mctx->rule, entry); ARRAY_TRUNC(&mctx->stack, 1); } return (error); }