961 lines
29 KiB
Nix
961 lines
29 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
vaculib,
|
|
...
|
|
}:
|
|
let
|
|
inherit (builtins)
|
|
isString
|
|
isList
|
|
length
|
|
head
|
|
all
|
|
isInt
|
|
isAttrs
|
|
isFloat
|
|
isBool
|
|
;
|
|
inherit (lib)
|
|
concatStrings
|
|
concatStringsSep
|
|
splitString
|
|
match
|
|
replaceStrings
|
|
reverseList
|
|
elemAt
|
|
mapAttrsToList
|
|
;
|
|
mapConcat = f: xs: concatStrings (map f xs);
|
|
mapConcatSep =
|
|
sep: f: xs:
|
|
concatStringsSep sep (map f xs);
|
|
mapConcatLines = f: xs: mapConcatSep "\n" f xs;
|
|
isListWhere = xs: f: (isList xs) && (all f xs);
|
|
stringOrList = val: (isString val) || ((isListWhere val isString) && (length val) > 0);
|
|
listify = val: if isList val then val else [ val ];
|
|
is_match = regex: s: (match regex s) != null;
|
|
is_not_match = regex: s: !(is_match regex s);
|
|
only_printable_ascii = s: is_match "[ -~\r\n]*" s;
|
|
has_vars = s: lib.hasInfix ("$" + "{") s;
|
|
sieve_raw_escape_string =
|
|
s:
|
|
if !only_printable_ascii s then
|
|
builtins.trace s throw "s failed only_printable_ascii check"
|
|
else
|
|
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s;
|
|
sieve_encode_string =
|
|
{
|
|
allow_vars,
|
|
for_debug_comment,
|
|
with_quotes,
|
|
}:
|
|
s:
|
|
assert isString s;
|
|
assert allow_vars || for_debug_comment || (!has_vars s);
|
|
let
|
|
a = sieve_raw_escape_string s;
|
|
b = if for_debug_comment then replaceStrings [ ''*/'' ] [ ''*\/'' ] a else a;
|
|
res = if with_quotes then ''"${b}"'' else b;
|
|
in
|
|
res;
|
|
sieve_quote_string = sieve_encode_string {
|
|
allow_vars = false;
|
|
for_debug_comment = false;
|
|
with_quotes = true;
|
|
};
|
|
sieve_quote_string_with_interp = sieve_encode_string {
|
|
allow_vars = true;
|
|
for_debug_comment = false;
|
|
with_quotes = true;
|
|
};
|
|
is_valid_long_ident = is_match "[a-z_][a-z0-9_]*";
|
|
is_number_ident = is_match "[0-9]*";
|
|
is_valid_ident = s: (is_valid_long_ident s) || (is_number_ident s);
|
|
interp =
|
|
ident:
|
|
assert isString ident;
|
|
assert is_valid_ident ident;
|
|
"$" + "{${ident}}";
|
|
dest = "envelope_to";
|
|
dest_domain = "envelope_to_domain";
|
|
set_envelope = ''
|
|
#set_envelope START
|
|
if header :index 1 :matches "X-Envelope-To" "*" {
|
|
set ${sieve_quote_string dest} "''${1}";
|
|
}
|
|
if header :index 1 :matches "X-Envelope-To" "*@*" {
|
|
set ${sieve_quote_string dest_domain} "''${2}";
|
|
}
|
|
#set_envelope END
|
|
'';
|
|
envelope_is =
|
|
key:
|
|
assert stringOrList key;
|
|
''string :is "${interp dest}" ${sieve_encode key}'';
|
|
envelope_matches =
|
|
key:
|
|
assert stringOrList key;
|
|
''string :matches "${interp dest}" ${sieve_encode key}'';
|
|
envelope_domain_is = key: ''string :is "${interp dest_domain}" ${sieve_quote_string key}'';
|
|
sieve_encode_list =
|
|
xs:
|
|
assert isListWhere xs isString;
|
|
"[ ${mapConcatSep ", " sieve_encode xs} ]";
|
|
sieve_encode =
|
|
val:
|
|
if isString val then
|
|
sieve_quote_string val
|
|
else if isList val then
|
|
sieve_encode_list val
|
|
else
|
|
assert "dunno what to do with this";
|
|
null;
|
|
sieve_debug_list = xs: "[ ${mapConcat (s: (sieve_debug s) + " ") xs}]";
|
|
sieve_debug_attrs =
|
|
attrs:
|
|
let
|
|
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
|
|
pairStrs = mapAttrsToList toPairStr attrs;
|
|
pairsStr = concatStrings pairStrs;
|
|
in
|
|
"{ ${pairsStr}}";
|
|
sieve_debug =
|
|
val:
|
|
if isString val then
|
|
sieve_encode_string {
|
|
allow_vars = true;
|
|
for_debug_comment = true;
|
|
with_quotes = true;
|
|
} val
|
|
else if (isInt val) || (isFloat val) then
|
|
toString val
|
|
else if (isBool val) then
|
|
(if val then "true" else "false")
|
|
else if isNull val then
|
|
"null"
|
|
else if isList val then
|
|
sieve_debug_list val
|
|
else if isAttrs val then
|
|
sieve_debug_attrs val
|
|
else
|
|
assert "dunno what to do with this";
|
|
null;
|
|
is_flagish =
|
|
flag_name:
|
|
let
|
|
# escape_all = map lib.escapeRegex;
|
|
|
|
# all from https://datatracker.ietf.org/doc/html/rfc9051#name-formal-syntax
|
|
# resp-specials = escape_all [ "]" ];
|
|
# DQUOTE = ''"'';
|
|
# quoted-specials = escape_all [ DQUOTE "\\" ];
|
|
# list-wildcards = escape_all [ "%" "*" ];
|
|
# CTL = something; # 0x00 thru 0x1F, and 0x7F
|
|
# SP = escape_all [ " " ];
|
|
# atom-specials = (escape_all [ "(" ")" "{" ]) ++ [ SP CTL list-wildcards quoted-specials resp-specials ];
|
|
# " " 0x20 !allowed
|
|
# "!" 0x21 ok
|
|
# "\"" 0x22 !allowed
|
|
# "#" 0x23 ok
|
|
# "$" 0x24 ok
|
|
# "%" 0x25 !allowed
|
|
# "&" 0x26 ok
|
|
# "'" 0x27 ok
|
|
# "(" 0x28 !allowed
|
|
# ")" 0x29 !allowed
|
|
# "*" 0x2a !allowed
|
|
# "+" 0x2b ok
|
|
# ...
|
|
# "Z" 0x5a ok
|
|
# "[" 0x5b !allowed
|
|
# "\\" 0x5c !allowed
|
|
# "]" 0x5d ok
|
|
# "^" 0x5e ok
|
|
# ...
|
|
# "z" 0x7a ok
|
|
# "{" 0x7b !allowed
|
|
# "|" 0x7c ok
|
|
# "}" 0x7d ok
|
|
# "~" 0x7e ok
|
|
# DEL 0x7f !allowed
|
|
# ATOM-CHAR = something; # "any CHAR except atom-specials"
|
|
ATOM-CHAR = ''[]!#$&'+-Z^-z|}~]'';
|
|
atom = "${ATOM-CHAR}+";
|
|
flag-keyword = ''\$MDNSent|\$Forwarded|\$Junk|\$NotJunk|\$Phishing|(${atom})'';
|
|
flag-extension = ''\\(${atom})'';
|
|
flag = ''\\Answered|\\Flagged|\\Deleted|\\Seen|\\Draft|(${flag-keyword})|(${flag-extension})'';
|
|
in
|
|
(isString flag_name) && ((builtins.match flag flag_name) != null);
|
|
known_flags = rec {
|
|
seen = ''\Seen'';
|
|
read = seen;
|
|
};
|
|
pure_flags_impl =
|
|
flags: conditions:
|
|
assert isListWhere flags isString;
|
|
assert isListWhere conditions isString;
|
|
assert (length flags) > 0;
|
|
assert (length conditions) > 0;
|
|
let
|
|
argAttrs = { inherit flags conditions; };
|
|
firstFlag = head flags;
|
|
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
|
|
in
|
|
''
|
|
# pure_flags ${sieve_debug argAttrs};
|
|
removeflag ${sieve_quote_string firstFlag};
|
|
if ${combined_condition} {
|
|
${record_action "pure_flags ${concatStringsSep " " flags}"}
|
|
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)}
|
|
}
|
|
# pure_flags end
|
|
'';
|
|
pure_flags =
|
|
flags: conditions:
|
|
assert stringOrList flags;
|
|
assert stringOrList conditions;
|
|
pure_flags_impl (listify flags) (listify conditions);
|
|
exists_impl =
|
|
headers:
|
|
assert isListWhere headers isString;
|
|
if headers == [ ] then
|
|
"/* exists START: called with empty array */ false /* exists END */"
|
|
else
|
|
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */";
|
|
exists =
|
|
headers:
|
|
assert stringOrList headers;
|
|
exists_impl (listify headers);
|
|
header_generic =
|
|
match_kind: header_s: match_es:
|
|
assert stringOrList header_s;
|
|
assert stringOrList match_es;
|
|
''/* header_generic START */ header ${match_kind} ${sieve_encode header_s} ${sieve_encode match_es} /* header_generic END */'';
|
|
header_matches = header_generic ":matches";
|
|
header_is = header_generic ":is";
|
|
subject_generic = match_kind: match_es: header_generic match_kind "Subject" match_es;
|
|
subject_matches = subject_generic ":matches";
|
|
subject_is = subject_generic ":is";
|
|
environment_generic =
|
|
match_kind: environment_name_s: match_es:
|
|
assert stringOrList environment_name_s;
|
|
assert stringOrList match_es;
|
|
"environment ${match_kind} ${sieve_encode environment_name_s} ${sieve_encode match_es}";
|
|
environment_matches = environment_generic ":matches";
|
|
environment_is = environment_generic ":is";
|
|
from_is =
|
|
addr_list:
|
|
assert stringOrList addr_list;
|
|
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */'';
|
|
from_matches =
|
|
addr_list:
|
|
assert stringOrList addr_list;
|
|
''/* from_is START */ address :matches :all "From" ${sieve_encode addr_list} /* from_is END */'';
|
|
var_is =
|
|
var_name: rhs:
|
|
assert isString var_name;
|
|
assert stringOrList rhs;
|
|
''string :is "''${${var_name}}" ${sieve_encode rhs}'';
|
|
var_is_true = var_name: var_is var_name "1";
|
|
var_is_false = var_name: not (var_is_true var_name);
|
|
has_flag =
|
|
flag_name:
|
|
assert isString flag_name;
|
|
assert is_flagish flag_name; # no spaces allowed in flag names
|
|
''hasflag :is ${sieve_encode flag_name}'';
|
|
set_with_interp =
|
|
var_name: new_val:
|
|
assert isString var_name;
|
|
assert is_valid_ident var_name;
|
|
assert isString new_val;
|
|
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};";
|
|
set =
|
|
var_name: new_val:
|
|
assert isString var_name;
|
|
assert is_valid_ident var_name;
|
|
assert isString new_val;
|
|
"set ${sieve_encode var_name} ${sieve_encode new_val};";
|
|
set_bool_var =
|
|
var_name: bool_val:
|
|
assert isBool bool_val;
|
|
set var_name (if bool_val then "1" else "0");
|
|
over_test_list =
|
|
name: test_list:
|
|
assert isListWhere test_list isString;
|
|
''
|
|
${name}(
|
|
${concatStringsSep ",\n" test_list}
|
|
)
|
|
'';
|
|
anyof = over_test_list "anyof";
|
|
allof = over_test_list "allof";
|
|
not = test: "not ${test}";
|
|
record_action =
|
|
action_desc:
|
|
assert isString action_desc;
|
|
''addheader "X-Vacu-Action" ${sieve_encode action_desc};'';
|
|
fileinto =
|
|
folder:
|
|
assert isString folder;
|
|
''
|
|
${record_action "fileinto ${folder}"}
|
|
fileinto :create ${sieve_encode folder};
|
|
'';
|
|
ihave =
|
|
extension_name_s:
|
|
assert stringOrList extension_name_s;
|
|
"ihave ${sieve_encode extension_name_s}";
|
|
# email_filters = map (e: ''
|
|
# elsif ${envelope_is e} { # item of email_filters
|
|
# ${record_action "email_filters fileinto ${mk_email_folder_name e}"}
|
|
# fileinto :create ${sieve_quote_string (mk_email_folder_name e)};
|
|
# }
|
|
# '') email_folders;
|
|
# domain_filters = map (d: ''
|
|
# elsif ${envelope_domain_is d} { # item of domain_filters
|
|
# ${record_action "domain_filters fileinto ${mk_domain_folder_name d}"}
|
|
# fileinto :create ${sieve_quote_string (mk_domain_folder_name d)};
|
|
# }
|
|
# '') domain_folders;
|
|
set_from =
|
|
{
|
|
condition,
|
|
var,
|
|
default ? "-",
|
|
warn_if_unset ? false,
|
|
}@args:
|
|
''
|
|
# set_from ${sieve_debug args}
|
|
if ${condition} {
|
|
${set_with_interp var (interp "1")}
|
|
}
|
|
else {
|
|
${lib.optionalString warn_if_unset (
|
|
maybe_debug "info: Could not set ${var} from condition ${condition}, setting to default(${default})"
|
|
)}
|
|
${set var default}
|
|
}
|
|
# set_from END
|
|
'';
|
|
set_var_from_environment =
|
|
item: var:
|
|
''
|
|
# set_var_from_environment
|
|
''
|
|
+ set_from {
|
|
condition = ''environment :matches ${sieve_quote_string item} "*"'';
|
|
inherit var;
|
|
};
|
|
maybe_debug = msg: ''
|
|
if ${ihave "vnd.dovecot.debug"} {
|
|
debug_log ${sieve_quote_string_with_interp msg};
|
|
}
|
|
'';
|
|
# trimmed down from https://pages.ebay.com/securitycenter/security_researchers_eligible_domains.html
|
|
ebay_domains = vaculib.listOfLines { } ''
|
|
ebay.com
|
|
ebay.co.uk
|
|
ebay.com.au
|
|
ebay.de
|
|
ebay.ca
|
|
ebay.fr
|
|
ebay.it
|
|
ebay.es
|
|
ebay.at
|
|
ebay.ch
|
|
ebay.com.hk
|
|
ebay.com.sg
|
|
ebay.com.my
|
|
ebay.in
|
|
ebay.ph
|
|
ebay.ie
|
|
ebay.pl
|
|
ebay.be
|
|
ebay.nl
|
|
ebay.cn
|
|
ebay.com.tw
|
|
ebay.co.jp
|
|
ebaythailand.co.th
|
|
'';
|
|
sieve_text = ''
|
|
require [
|
|
"fileinto",
|
|
"mailbox",
|
|
"imap4flags",
|
|
"editheader",
|
|
"environment",
|
|
"variables",
|
|
"date",
|
|
"index",
|
|
"ihave"
|
|
];
|
|
|
|
if ${
|
|
allof [
|
|
(ihave "imapsieve")
|
|
(environment_matches "imap.user" "*")
|
|
(environment_matches "location" "MS")
|
|
(environment_matches "phase" "post")
|
|
]
|
|
} {
|
|
${set_bool_var "in_imap" true}
|
|
} else {
|
|
${set_bool_var "in_imap" false}
|
|
}
|
|
|
|
if ${var_is_true "in_imap"} {
|
|
if ${
|
|
not (allof [
|
|
(environment_is "imap.cause" [
|
|
"APPEND"
|
|
"COPY"
|
|
""
|
|
])
|
|
(environment_is "imap.mailbox" [
|
|
"MagicRefilter"
|
|
""
|
|
])
|
|
])
|
|
} {
|
|
${maybe_debug "NOT doing anything cuz imap.cause and/or imap.mailbox isn't right"}
|
|
stop;
|
|
}
|
|
}
|
|
|
|
${set_envelope}
|
|
${set_var_from_environment "location" "env_location"}
|
|
${set_var_from_environment "phase" "env_phase"}
|
|
${set_var_from_environment "imap.user" "env_imap_user"}
|
|
${set_var_from_environment "imap.email" "env_imap_email"}
|
|
${set_var_from_environment "imap.cause" "env_imap_cause"}
|
|
${set_var_from_environment "imap.mailbox" "env_imap_mailbox"}
|
|
${set_var_from_environment "imap.changedflags" "env_imap_changedflags"}
|
|
${set_from {
|
|
condition = ''currentdate :matches "iso8601" "*"'';
|
|
var = "datetime";
|
|
}}
|
|
${set_with_interp "sieved_message" ''at ''${datetime} by ${config.vacu.versionId} loc ''${env_location} phase ''${env_phase} user ''${env_imap_user} email ''${env_imap_email} cause ''${env_imap_cause} mailbox ''${env_imap_mailbox} changedflags ''${env_imap_changedflags} envelope ''${dest}''}
|
|
${maybe_debug ''X-Vacu-Sieved: ''${sieved_message}''}
|
|
|
|
if ${ihave "envelope"} {
|
|
if envelope :all :matches "to" "*@*" {
|
|
${set_with_interp "userfor" (interp "1")}
|
|
} else {
|
|
error "i dunno what to do, theres no envelope";
|
|
}
|
|
}
|
|
elsif ${var_is_true "in_imap"} {
|
|
${set_with_interp "userfor" (interp "env_imap_user")}
|
|
}
|
|
else {
|
|
error "dont have envelope or imapsieve, dunno what to do";
|
|
}
|
|
|
|
if ${var_is "userfor" "shelvacu"} {
|
|
addheader "X-Vacu-Sieved" "''${sieved_message}";
|
|
removeflag "not-spamish";
|
|
removeflag "orders";
|
|
removeflag "banking";
|
|
removeflag "banking-statements";
|
|
removeflag "banking-transactions";
|
|
removeflag "A";
|
|
removeflag "B";
|
|
removeflag "B.subscriptions";
|
|
removeflag "C";
|
|
removeflag "D";
|
|
|
|
${pure_flags [ "wells-fargo" "banking" ] (envelope_is "wf-primary@shelvacu.com")}
|
|
${pure_flags
|
|
[ "wells-fargo-transactions" "banking-transactions" "B" ]
|
|
[
|
|
(has_flag "wells-fargo")
|
|
(subject_matches [
|
|
"You just got paid!"
|
|
"Wells Fargo card purchase exceeded preset amount"
|
|
"You made a payment"
|
|
"You made a credit card purchase of *"
|
|
"Your card wasn't present for a purchase"
|
|
"Account update"
|
|
"You've earned cash back from My Wells Fargo Deals"
|
|
"Confirmation of your Wells Fargo Rewards redemption"
|
|
"You sent money with Zelle(R)"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "wells-fargo-statements" "banking-statements" "C" ]
|
|
[
|
|
(has_flag "wells-fargo")
|
|
(subject_matches [
|
|
"Your statement for credit card account *"
|
|
"Your statement for account *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "wells-fargo-action-required" "A" ]
|
|
[
|
|
# wf is actually careful about saying action required
|
|
(has_flag "wells-fargo")
|
|
(subject_matches "Action Required: *")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "wells-fargo-misc" "A" ]
|
|
[
|
|
(has_flag "wells-fargo")
|
|
(not (has_flag "wells-fargo-transactions"))
|
|
(not (has_flag "wells-fargo-statements"))
|
|
(not (has_flag "wells-fargo-action-required"))
|
|
]
|
|
}
|
|
${pure_flags [ "chase" "banking" ] (envelope_is "chase@shelvacu.com")}
|
|
${pure_flags
|
|
[ "chase-transactions" "banking-transactions" "B" ]
|
|
[
|
|
(has_flag "chase")
|
|
(subject_matches [
|
|
"Your * payment is scheduled"
|
|
"You made a * transaction with *"
|
|
"Your * transaction with *"
|
|
"Chase security alert: You signed in with a new device"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "chase-statements" "banking-statements" "C" ]
|
|
[
|
|
(has_flag "chase")
|
|
(subject_matches [
|
|
"Your credit card statement is available"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "chase-spam" "D" ]
|
|
[
|
|
(has_flag "chase")
|
|
(anyof [
|
|
(header_is "From" "Chase Credit Journey <no.reply.alerts@chase.com>")
|
|
(subject_is [
|
|
"Review your recent activity"
|
|
"Good news: You may qualify for a credit line increase!"
|
|
"Your Chase card is available to use with Paze - Activate now!"
|
|
])
|
|
])
|
|
]
|
|
}
|
|
${pure_flags [ "experian" ] (envelope_is "fbyjemby@shelvacu.com")}
|
|
${pure_flags
|
|
[ "experian-spam" "D" ]
|
|
[
|
|
(has_flag "experian")
|
|
(subject_matches [
|
|
"*, your FICO* Score has been updated"
|
|
"Your monthly account statement is here, *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "paypal" "banking" ]
|
|
[
|
|
# can't go purely on envelope, because paypal loves to give my email to every merchant I interact with
|
|
(envelope_is "paypal@shelvacu.com")
|
|
(from_matches [
|
|
"*@paypal.com"
|
|
"*@*.paypal.com"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "paypal-transactions" "banking-transactions" "B" ]
|
|
[
|
|
(has_flag "paypal")
|
|
(subject_matches [
|
|
"Receipt for your payment to *"
|
|
"*: $* USD"
|
|
"*: $* CAD"
|
|
"*: kr * SEK"
|
|
"You authorized a payment to *"
|
|
"You sent an automatic payment to *"
|
|
"Review your new automatic payment setup for *"
|
|
"You have a refund from *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "paypal-statements" "banking-statements" "C" ]
|
|
[
|
|
(has_flag "paypal")
|
|
(subject_matches [
|
|
"*, your * account statement is available."
|
|
])
|
|
]
|
|
}
|
|
|
|
${pure_flags [ "usps-id" ] (envelope_is "usps-id@shelvacu.com")}
|
|
${pure_flags
|
|
[ "usps-expected-delivery" "C" ]
|
|
[
|
|
(has_flag "usps-id")
|
|
(subject_matches "USPS* Expected Delivery *")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "amazon-ignore" "C" ]
|
|
[
|
|
(envelope_is "amznbsns@shelvacu.com")
|
|
(subject_matches [
|
|
"Your Amazon.com order has shipped*"
|
|
"Your Amazon.com order of * has shipped!"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "bandcamp-ignore" "C" ]
|
|
[
|
|
(envelope_is "bandcamp@shelvacu.com")
|
|
(subject_matches [
|
|
"* just announced a listening party on Bandcamp"
|
|
"New items from *"
|
|
"Starting in *"
|
|
"New from *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "bandcamp-not-ignore" "B.subscriptions" ]
|
|
[
|
|
(envelope_is "bandcamp@shelvacu.com")
|
|
''not hasflag "bandcamp-ignore"''
|
|
]
|
|
}
|
|
${pure_flags [ "ika-ignore" "D" ] (envelope_is "ika@dis8.net")}
|
|
${pure_flags
|
|
[ "ally-statement" "C" ]
|
|
[
|
|
(envelope_is "ally@shelvacu.com")
|
|
(subject_is "Your latest statement is ready to view.")
|
|
]
|
|
}
|
|
|
|
${pure_flags "bloomberg" (envelope_is "bloomberg@shelvacu.com")}
|
|
|
|
${pure_flags
|
|
[ "money-stuff" "not-spamish" ]
|
|
[
|
|
(envelope_is "bloomberg@shelvacu.com")
|
|
''header :matches "From" "\"Matt Levine\" *"''
|
|
]
|
|
}
|
|
|
|
${pure_flags
|
|
[ "money-stuff-podcast" "D" known_flags.read ]
|
|
[
|
|
(has_flag "money-stuff")
|
|
(subject_matches "Money Stuff: The Podcast:*")
|
|
]
|
|
}
|
|
|
|
${pure_flags
|
|
[ "money-stuff-not-podcast" "B.subscriptions" ]
|
|
[
|
|
(has_flag "money-stuff")
|
|
(not (has_flag "money-stuff-podcast"))
|
|
]
|
|
}
|
|
|
|
${pure_flags [ "git" "not-spamish" "B" ] (exists [
|
|
"X-GitHub-Reason"
|
|
"X-GitLab-Project"
|
|
])}
|
|
${pure_flags [ "git-uninsane" "git" "not-spamish" "B" ] (envelope_is "git-uninsane@shelvacu.com")}
|
|
${pure_flags [ "github" "git" "not-spamish" "B" ] (header_matches "List-Id" "*<*.github.com>")}
|
|
${pure_flags [ "mailing-list-by-envelope" "not-spamish" "B" ] (
|
|
envelope_matches "*-ml@shelvacu.com"
|
|
)}
|
|
|
|
${pure_flags [ "discourse" "not-spamish" "B" ] (exists "X-Discourse-Post-Id")}
|
|
${pure_flags [ "agora" "not-spamish" ] (envelope_is "agora@shelvacu.com")}
|
|
${pure_flags [ "postgres-list" "not-spamish" ] (
|
|
header_matches "List-Id" "<*.lists.postgresql.org>"
|
|
)}
|
|
${pure_flags [ "secureaccesswa" "not-spamish" "A" ] (from_is "help@secureaccess.wa.gov")}
|
|
${pure_flags [ "letsencrypt-mailing-list" "not-spamish" "B" ] (
|
|
envelope_is "lets-encrypt-mailing-list@shelvacu.com"
|
|
)}
|
|
${pure_flags [ "jmp-news" "not-spamish" "B" ] (header_matches "List-Id" "*<jmp-news.soprani.ca>")}
|
|
${pure_flags
|
|
[ "tf2wiki" "not-spamish" "B" ]
|
|
[
|
|
(envelope_is "tf2wiki@shelvacu.com")
|
|
(from_is "noreply@wiki.teamfortress.com")
|
|
]
|
|
}
|
|
|
|
${pure_flags "gmail-fwd" (envelope_is "gmailfwd-fc2e10bec8b2@shelvacu.com")}
|
|
${pure_flags [ "ebay" "orders" ] (envelope_is "ebay@shelvacu.com")}
|
|
${pure_flags
|
|
[ "ebay-delivered" "B" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(subject_matches [
|
|
"*ORDER DELIVERED: *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "ebay-message" "B" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(from_matches (map (domain: "*@members.${domain}") ebay_domains))
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "ebay-offer" "B" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(subject_matches [
|
|
"You have an offer from the seller, *"
|
|
"You saw it at *, but the seller is now offering *"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "ebay-order-update" "C" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(subject_matches [
|
|
"Out for delivery: *"
|
|
"*DELIVERY UPDATE: *"
|
|
"*Order update: *"
|
|
"EARLY DELIVERY UPDATE: *"
|
|
"Important information regarding your Global Shipping Program transaction *" # ebay: "important information! your order is being shipped." why did you say this was ""important""???
|
|
"Your package is now with *"
|
|
"*Order confirmed: *"
|
|
"Your order is confirmed"
|
|
"Your order is in!"
|
|
"*An update on your order"
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "ebay-bid-ongoing-notification" "C" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(subject_matches [
|
|
"Michael, your bid for * is winning"
|
|
"* just got a new bid."
|
|
])
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "ebay-feedback" "D" ]
|
|
[
|
|
(has_flag "ebay")
|
|
(subject_matches "Please provide feedback for your eBay items")
|
|
]
|
|
}
|
|
${pure_flags [ "royal-mail" "orders" ] (from_is "no-reply@royalmail.com")}
|
|
${pure_flags
|
|
[ "royal-mail-delivered" "B" ]
|
|
[
|
|
(has_flag "royal-mail")
|
|
(subject_matches "Your Royal Mail parcel has been delivered")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "royal-mail-on-the-way" "D" ]
|
|
[
|
|
(has_flag "royal-mail")
|
|
(subject_matches "Your Royal Mail parcel is on its way")
|
|
]
|
|
}
|
|
${pure_flags [ "aliexpress" "orders" ] (from_is [
|
|
"transaction@notice.aliexpress.com"
|
|
"aliexpress@notice.aliexpress.com"
|
|
])}
|
|
${pure_flags
|
|
[ "aliexpress-delivered" "B" ]
|
|
[
|
|
(has_flag "aliexpress")
|
|
(from_is "transaction@notice.aliexpress.com")
|
|
(subject_matches "Order * has been signed for")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "aliexpress" "orders" "C" ]
|
|
[
|
|
(has_flag "aliexpress")
|
|
(not (has_flag "aliexpress-delivered"))
|
|
]
|
|
}
|
|
${pure_flags [ "brandcrowd" "D" ] (envelope_is "brandcrowd@shelvacu.com")}
|
|
${pure_flags [ "cpapsupplies" "D" ] (envelope_is "cpapsupplies@shelvacu.com")}
|
|
${pure_flags [ "genshin" "D" ] (envelope_is "genshin@shelvacu.com")}
|
|
${pure_flags [ "jork" "B" ] (envelope_is "jork@shelvacu.com")}
|
|
${pure_flags [ "patreon" "not-spamish" ] (envelope_is "patreon@shelvacu.com")}
|
|
${pure_flags
|
|
[ "patreon-post" "B.subscriptions" ]
|
|
[
|
|
(has_flag "patreon")
|
|
(header_is "X-Mailgun-Tag" "template_newsletterpostcontrol")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "patreon-free-member-digest" "D" ]
|
|
[
|
|
(has_flag "patreon")
|
|
(header_is "X-Mailgun-Tag" "template_freememberdigest")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "patreon-other" "B" ]
|
|
[
|
|
(has_flag "patreon")
|
|
(not (has_flag "patreon-post"))
|
|
(not (has_flag "patreon-free-member-digest"))
|
|
]
|
|
}
|
|
${pure_flags [ "rsb" "B" ] (from_is "support@rapidseedbox.com")}
|
|
${pure_flags [ "fresh-avocado-dis8" "D" ] (envelope_is "fresh.avocado@dis8.net")}
|
|
${pure_flags [ "discord" "A" ] (envelope_matches "discord@*")}
|
|
${pure_flags [ "za-sa" "D" ] (from_matches [
|
|
"*@*.sa.com"
|
|
"*@*.za.com"
|
|
])}
|
|
${pure_flags [ "localdomain" "D" ] (from_matches [
|
|
"*@*.local"
|
|
"*@*.localdomain"
|
|
])}
|
|
${pure_flags [ "helium" "D" ] (envelope_is "creepyface@dis8.net")}
|
|
${pure_flags [ "sharkmood" "C" ] (envelope_is "sharkmood@dis8.net")}
|
|
${pure_flags [ "im-not-district-158" "D" ] (envelope_is [
|
|
"khamar.anderson@dis8.net"
|
|
"pbooth@dis8.net"
|
|
"sgaylor@dis8.net"
|
|
])}
|
|
${pure_flags [ "next-level-burger" "D" ] (header_matches "From" "*Next Level Burger*")}
|
|
${pure_flags [ "lyft" "D" ] (envelope_is "lyft@shelvacu.com")}
|
|
${pure_flags [ "coursera" "D" ] (from_matches "*.*.coursera.org")}
|
|
${pure_flags [ "taskrabbit" "D" ] (envelope_is "taskrabbit@shelvacu.com")}
|
|
${pure_flags [ "subscribestar_code" "A" ] (allof [
|
|
(envelope_is "subscribestar@shelvacu.com")
|
|
(subject_is "Your authentication code")
|
|
])}
|
|
${pure_flags "itch-io" (from_is "postmaster@itch.io")}
|
|
${pure_flags
|
|
[ "itch-io-update" "B.subscriptions" ]
|
|
[
|
|
(has_flag "itch-io")
|
|
(subject_matches "[itch.io] * update *")
|
|
]
|
|
}
|
|
${pure_flags
|
|
[ "lowering-the-bar" "B.subscriptions" ]
|
|
[
|
|
(envelope_is "ltb@shelvacu.com")
|
|
]
|
|
}
|
|
${pure_flags [ "hotels-com" "D" ] (from_matches [
|
|
"hotels.com"
|
|
"*.hotels.com"
|
|
])}
|
|
|
|
${pure_flags
|
|
[ "spamish-by-headers" "C" ]
|
|
[
|
|
(anyof [
|
|
(header_is "Precedence" "bulk")
|
|
(exists "List-Unsubscribe")
|
|
(exists "List-Unsubscribe-Post")
|
|
])
|
|
(not (has_flag "not-spamish"))
|
|
]
|
|
}
|
|
|
|
if hasflag "agora" {
|
|
${fileinto "M.agora"}
|
|
} elsif hasflag "postgres-list" {
|
|
${fileinto "M.postgres"}
|
|
} elsif hasflag "D" {
|
|
${fileinto "D"}
|
|
} elsif hasflag "C" {
|
|
${fileinto "C"}
|
|
} elsif hasflag "A" {
|
|
${fileinto "A"}
|
|
} elsif hasflag "B.subscriptions" {
|
|
${fileinto "B.subscriptions"}
|
|
} else {
|
|
${fileinto "B"}
|
|
}
|
|
}
|
|
# disable any sieve scripts that might want to run after this one
|
|
stop;
|
|
'';
|
|
pigeonhole_pkg = pkgs.dovecot_pigeonhole;
|
|
in
|
|
{
|
|
imports = [
|
|
# Allow running a sieve filter when a message gets moved to another folder in imap
|
|
# see https://doc.dovecot.org/2.3/configuration_manual/sieve/plugins/imapsieve/
|
|
{
|
|
services.dovecot2 = {
|
|
sieve.plugins = [ "sieve_imapsieve" ];
|
|
mailPlugins.perProtocol.imap.enable = [ "imap_sieve" ];
|
|
};
|
|
}
|
|
];
|
|
options.vacu.checkSieve = lib.mkOption {
|
|
readOnly = true;
|
|
default = pkgs.writeScriptBin "check-liam-sieve" ''
|
|
set -xev
|
|
${lib.escapeShellArgs [
|
|
(lib.getExe' pigeonhole_pkg "sieve-test")
|
|
"-c"
|
|
config.services.dovecot2.configFile
|
|
"-C" # force compilation
|
|
"-D" # enable sieve debugging
|
|
"-f"
|
|
"some-rando@example.com"
|
|
"-a"
|
|
"shelvacu@liam.dis8.net"
|
|
config.services.dovecot2.sieve.scripts.before
|
|
"/dev/null"
|
|
]}
|
|
'';
|
|
defaultText = "check-liam-sieve package";
|
|
};
|
|
options.vacu.liam-sieve-script = lib.mkOption {
|
|
readOnly = true;
|
|
default = pkgs.writeText "mainsieve" sieve_text;
|
|
defaultText = "mainsieve text package";
|
|
};
|
|
config = {
|
|
vacu.packages = [ pigeonhole_pkg ];
|
|
services.dovecot2.sieve = {
|
|
extensions = [
|
|
"fileinto"
|
|
"mailbox"
|
|
"editheader"
|
|
"vnd.dovecot.debug"
|
|
];
|
|
scripts.before = config.vacu.liam-sieve-script;
|
|
};
|
|
services.dovecot2.imapsieve.mailbox = [
|
|
{
|
|
name = "*";
|
|
causes = [
|
|
"APPEND"
|
|
"COPY"
|
|
"FLAG"
|
|
];
|
|
before = config.vacu.liam-sieve-script;
|
|
}
|
|
];
|
|
# services.dovecot2.mailboxes."magic-refilter".auto = "create";
|
|
};
|
|
}
|