123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127openCoreopenAsync_smtpmoduleRegex=Re2letreadme="The query language is the Blang language:\n\n\
B ::=\n\
\ | envelope_sender REGEX\n\
\ | envelope_recipient REGEX\n\
\ | recipient REGEX\n\
\ | subject REGEX\n\
\ | rfc822_id REGEX\n\
\ | flows FLOWS\n\n\
E ::=\n\
\ | True\n\
\ | False\n\
\ | (and E E ...)\n\
\ | (or E E ...)\n\
\ | (not E)\n\
\ | (if E E E)\n\
\ | (B)\n\n\
Examples:\n\n\
(and (recipient foo) (envelope_sender bar))\n\
(subject \"Awesome email subject\")\n";;moduleMail_fingerprint=Smtp_mail_log.Mail_fingerprintmoduleBase=structtypet=[`envelope_senderofRegex.t|`envelope_recipientofRegex.t|`recipientofRegex.t|`subjectofRegex.t|`rfc822_idofRegex.t|`flowsofSmtp_mail_log.Stable.Flows.V1.t][@@derivingsexp]letregex=function|`envelope_senderregex|`envelope_recipientregex|`recipientregex|`subjectregex|`rfc822_idregex->regex|`flows(flows:Smtp_mail_log.Flows.t)->List.map(flows:>stringlist)~f:Regex.escape|>List.map~f:(sprintf"(%s)")|>String.concat~sep:"|"|>Regex.of_string;;letmatches_message_headermsgheaderregex=Option.value_map(Smtp_mail_log.Message.emailmsg)~default:false~f:(funmsg->List.exists(Mail_fingerprint.headersmsg)~f:(fun(name,value)->String.Caseless.equalheadername&&Regex.matchesregexvalue));;letmatches_messagetmsg=matchtwith|`envelope_senderregex->Smtp_mail_log.Message.sendermsg|>Option.map~f:(function|`Stringstr->str|`Sendersender->Smtp_envelope.Sender.to_stringsender)|>Option.value_map~default:false~f:(Regex.matchesregex)|`envelope_recipientregex|`recipientregex->Smtp_mail_log.Message.recipientsmsg|>Option.value~default:[]|>List.map~f:(function|`Stringstr->str|`Emailemail->Email_address.to_stringemail)|>List.exists~f:(Regex.matchesregex)|`subjectregex->matches_message_headermsg"Subject"regex|`rfc822_idregex->matches_message_headermsg"Message-Id"regex||Smtp_mail_log.Message.rfc822_idmsg|>Option.value_map~default:false~f:(Regex.matchesregex)|`flowsflows->Smtp_mail_log.Flows.are_relatedflows(Smtp_mail_log.Message.flowsmsg);;endtypet=Base.tBlang.t[@@derivingsexp]letreccnf=function|Blang.True->[](* The Empty conjunction is true *)|False->[[]](* The Empty disjunction is false *)|Baseb->[[`Baseb]]|And(q1,q2)->cnfq1@cnfq2|Or(q1,q2)->List.map(List.cartesian_product(cnfq1)(cnfq2))~f:(fun(p,q)->p@q)|Notq->(matchqwith|Baseb->[[`Not_baseb]]|True->cnfBlang.false_|False->cnfBlang.true_|Notq->cnfq|And(q1,q2)->cnf(Blang.or_[Blang.not_q1;Blang.not_q2])|Or(q1,q2)->cnf(Blang.and_[Blang.not_q1;Blang.not_q2])|If(c,q1,q2)->cnf(Blang.if_c(Blang.not_q1)(Blang.not_q2)))|If(c,qt,qf)->(* Using the disjunctive representation of [If].
Its more intuitive, and its cnf rewrite contains additional
non-negated disjuncts that make for a better [permissive_cnf] *)cnf(Blang.or_[Blang.and_[c;qt];Blang.and_[Blang.not_c;qf]]);;letpermissive_cnft=letreduce_disjunction=letrecloopacc=function|`Baset::ts->loop(t::acc)ts|`Not_base_::_->None|[]->Some(List.revacc)inloop[]inlett=List.filter_map(cnft)~f:reduce_disjunctionin(* Short circuit the case that there is a [False] in the conjunction *)ifList.memt[]~equal:Poly.equalthen[[]]elset;;letarg=Command.Arg_type.create(funs->t_of_sexp(Sexp.of_strings))letmatches_messagetsession=Blang.evalt(funbase->Base.matches_messagebasesession);;