@@ -52,7 +52,17 @@ local settings = { | |||
use_bloom = false, | |||
symbol = 'KNOWN_SENDER', | |||
symbol_unknown = 'UNKNOWN_SENDER', | |||
symbol_check_mail_global = 'INC_MAIL_KNOWN_GLOBALLY', | |||
symbol_check_mail_local = 'INC_MAIL_KNOWN_LOCALLY', | |||
max_recipients = 15, | |||
redis_key = 'rs_known_senders', | |||
sender_prefix = 'rsrk', | |||
sender_key_global = 'verified_senders', | |||
sender_key_size = 20, | |||
reply_sender_privacy = false, | |||
reply_sender_privacy_alg = 'blake2', | |||
reply_sender_privacy_prefix = 'obf', | |||
reply_sender_privacy_length = 16, | |||
} | |||
local settings_schema = lua_redis.enrich_schema({ | |||
@@ -72,6 +82,43 @@ local function make_key(input) | |||
return hash:hex() | |||
end | |||
local function make_key_replies(goop, sz, prefix) | |||
local h = rspamd_cryptobox_hash.create() | |||
h:update(goop) | |||
local key = (prefix or '') .. h:base32():sub(1, sz) | |||
return key | |||
end | |||
local zscore_script_id | |||
local function configure_scripts(_, _, _) | |||
-- script checks if given recipients are in the local replies set of the sender | |||
local redis_zscore_script = [[ | |||
local replies_recipients_addrs = ARGV | |||
if replies_recipients_addrs ~= nil then | |||
for _, rcpt in ipairs(replies_recipients_addrs) do | |||
local score = redis.call('ZSCORE', KEYS[1], rcpt) | |||
-- check if score is nil (for some reason redis script does not see if score is a nil value) | |||
if type(score) == 'boolean' then | |||
score = nil | |||
-- 0 is stand for failure code | |||
return 0 | |||
end | |||
end | |||
-- first number in return statement is stands for the success/failure code | |||
-- where success code is 1 and failure code is 0 | |||
return 1 | |||
else | |||
-- 0 is a failure code | |||
return 0 | |||
end | |||
]] | |||
local zscore_script = lua_util.jinja_template(redis_zscore_script, { }) | |||
rspamd_logger.debugm(N, rspamd_config, 'added check for recipients in local replies set script %s', zscore_script) | |||
zscore_script_id = lua_redis.add_redis_script(zscore_script, redis_params) | |||
end | |||
local function check_redis_key(task, key, key_ty) | |||
lua_util.debugm(N, task, 'check key %s, type: %s', key, key_ty) | |||
local function redis_zset_callback(err, data) | |||
@@ -197,6 +244,94 @@ local function known_senders_callback(task) | |||
end | |||
end | |||
local function verify_local_replies_set(task) | |||
local replies_sender = task:get_reply_sender() | |||
if not replies_sender then | |||
lua_util.debugm(N, task, 'Could not get sender') | |||
return nil | |||
end | |||
local replies_recipients = task:get_recipients('mime') | |||
local replies_sender_string = lua_util.maybe_obfuscate_string(tostring(replies_sender), settings, settings.sender_prefix) | |||
local replies_sender_key = make_key_replies(replies_sender_string:lower(), 8) | |||
local function redis_zscore_script_cb(err, data) | |||
if err ~= nil then | |||
rspamd_logger.errx(task, 'Could not verify %s local replies set %s', replies_sender_key, err) | |||
end | |||
rspamd_logger.infox(task, 'DATA: %s', data) | |||
if data ~= 1 then | |||
rspamd_logger.infox(task, 'Recipients were not verified') | |||
return | |||
end | |||
rspamd_logger.infox(task, 'Recipients were verified') | |||
task:insert_result(settings.symbol_check_mail_local, 1.0, replies_sender_key) | |||
end | |||
local replies_recipients_addrs = {} | |||
-- assigning addresses of recipients for params and limiting number of recipients to be checked | |||
for i, rcpt in ipairs(replies_recipients) do | |||
if i > settings['max_recipients'] then | |||
break | |||
end | |||
table.insert(replies_recipients_addrs, rcpt.addr) | |||
end | |||
lua_util.debugm(N, task, 'Making redis request to local replies set') | |||
lua_redis.exec_redis_script(zscore_script_id, | |||
{task = task, is_write = true}, | |||
redis_zscore_script_cb, | |||
{ replies_sender_key }, | |||
replies_recipients_addrs ) | |||
end | |||
local function check_known_incoming_mail_callback(task) | |||
local replies_sender = task:get_reply_sender() | |||
if not replies_sender then | |||
lua_util.debugm(N, task, 'Could not get sender') | |||
return nil | |||
end | |||
-- making sender key | |||
lua_util.debugm(N, task, 'Sender: %s', replies_sender) | |||
local replies_sender_string = lua_util.maybe_obfuscate_string(tostring(replies_sender), settings, settings.sender_prefix) | |||
local replies_sender_key = make_key_replies(replies_sender_string:lower(), 8) | |||
lua_util.debugm(N, task, 'Sender key: %s', replies_sender_key) | |||
local function redis_zscore_global_cb(err, data) | |||
if err ~= nil then | |||
rspamd_logger.errx(task, 'Couldn\'t find sender %s in global replies set. Ended with error: %s', replies_sender, err) | |||
return | |||
end | |||
--checking if zcore have not found score of a sender | |||
if data ~= nil and data ~= '' and type(data) ~= 'userdata' then | |||
rspamd_logger.infox(task, 'Sender: %s verified. Output: %s', replies_sender, data) | |||
task:insert_result(settings.symbol_check_mail_global, 1.0, replies_sender) | |||
else | |||
rspamd_logger.infox(task, 'Sender: %s was not verified', replies_sender) | |||
end | |||
end | |||
-- key for global replies set | |||
local replies_global_key = make_key_replies(settings.sender_key_global, settings.sender_key_size, settings.sender_prefix) | |||
-- using zscore to find sender in global set | |||
lua_util.debugm(N, task, 'Making redis request to global replies set') | |||
lua_redis.redis_make_request(task, | |||
redis_params, -- connect params | |||
replies_sender_key, -- hash key | |||
false, -- is write | |||
redis_zscore_global_cb, --callback | |||
'ZSCORE', -- command | |||
{ replies_global_key, replies_sender } -- arguments | |||
) | |||
end | |||
local opts = rspamd_config:get_all_opt('known_senders') | |||
if opts then | |||
settings = lua_util.override_defaults(settings, opts) | |||
@@ -210,7 +345,8 @@ if opts then | |||
if redis_params then | |||
local map_conf = settings.domains | |||
settings.domains = lua_maps.map_add_from_ucl(settings.domains, 'set', 'domains to track senders from') | |||
settings.domains = lua_maps.map_add_from_ucl(settings.domains, | |||
'set', 'domains to track senders from') | |||
if not settings.domains then | |||
rspamd_logger.errx(rspamd_config, "couldn't add map %s, disable module", | |||
map_conf) | |||
@@ -221,6 +357,8 @@ if opts then | |||
'Known elements redis key', { | |||
type = 'zset/bloom filter', | |||
}) | |||
lua_redis.register_prefix(settings.sender_prefix, N, | |||
'Prefix to identify replies sets') | |||
local id = rspamd_config:register_symbol({ | |||
name = settings.symbol, | |||
type = 'normal', | |||
@@ -230,6 +368,20 @@ if opts then | |||
augmentations = { string.format("timeout=%f", redis_params.timeout or 0.0) } | |||
}) | |||
rspamd_config:register_symbol({ | |||
name = settings.symbol_check_mail_local, | |||
type = 'normal', | |||
callback = verify_local_replies_set, | |||
score = 1.0 | |||
}) | |||
rspamd_config:register_symbol({ | |||
name = settings.symbol_check_mail_global, | |||
type = 'normal', | |||
callback = check_known_incoming_mail_callback, | |||
score = 1.0 | |||
}) | |||
if settings.symbol_unknown and #settings.symbol_unknown > 0 then | |||
rspamd_config:register_symbol({ | |||
name = settings.symbol_unknown, | |||
@@ -243,3 +395,7 @@ if opts then | |||
lua_util.disable_module(N, "redis") | |||
end | |||
end | |||
rspamd_config:add_post_init(function(cfg, ev_base, worker) | |||
configure_scripts(cfg, ev_base, worker) | |||
end) |
@@ -34,9 +34,14 @@ local settings = { | |||
expire = 86400, -- 1 day by default | |||
key_prefix = 'rr', | |||
key_size = 20, | |||
sender_prefix = 'rsrk', | |||
sender_key_global = 'verified_senders', | |||
sender_key_size = 20, | |||
message = 'Message is reply to one we originated', | |||
symbol = 'REPLY', | |||
score = -4, -- Default score | |||
max_local_size = 20, | |||
max_global_size = 30, | |||
use_auth = true, | |||
use_local = true, | |||
cookie = nil, | |||
@@ -44,6 +49,10 @@ local settings = { | |||
cookie_is_pattern = false, | |||
cookie_valid_time = '2w', -- 2 weeks by default | |||
min_message_id = 2, -- minimum length of the message-id header | |||
reply_sender_privacy = false, | |||
reply_sender_privacy_alg = 'blake2', | |||
reply_sender_privacy_prefix = 'obf', | |||
reply_sender_privacy_length = 16, | |||
} | |||
local N = "replies" | |||
@@ -51,25 +60,81 @@ local N = "replies" | |||
local function make_key(goop, sz, prefix) | |||
local h = hash.create() | |||
h:update(goop) | |||
local key | |||
if sz then | |||
key = h:base32():sub(1, sz) | |||
else | |||
key = h:base32() | |||
end | |||
local key = (prefix or '') .. h:base32():sub(1, sz) | |||
return key | |||
end | |||
if prefix then | |||
key = prefix .. key | |||
end | |||
local global_replies_set_script | |||
local local_replies_set_script | |||
local function configure_redis_scripts(_, _) | |||
local redis_script_zadd_global = [[ | |||
-- getting last score of the recipients from the global replies set | |||
local data = {} | |||
data = redis.call('ZRANGE', KEYS[1], '-1', '-1', 'WITHSCORES') | |||
local score = tonumber(data[#data]) | |||
if score == nil then | |||
score = 0 | |||
end | |||
return key | |||
local global_size = redis.call('ZCARD', KEYS[1]) | |||
if global_size > {= mgs =} then | |||
redis.call('ZREMRANGEBYRANK', KEYS[1], '{= mgs =}', global_size) | |||
end | |||
local recipients_addrs = ARGV | |||
if recipients_addrs ~= nil then | |||
local params = {} | |||
--table.insert(params, KEYS[1]) | |||
for _, rcpt in ipairs(recipients_addrs) do | |||
-- score is using here as a one-based numeration just like in lua | |||
score = score + 1 | |||
table.insert(params, score) | |||
table.insert(params, tostring(rcpt)) | |||
end | |||
-- adding recipients to the global replies set | |||
redis.call('ZADD', KEYS[1], unpack(params)) | |||
end | |||
]] | |||
local set_script_zadd_global = lua_util.jinja_template(redis_script_zadd_global, | |||
{ mgs = settings['max_global_size'] }) | |||
rspamd_logger.debugm(N, rspamd_config, 'added add to global replies set script %s', set_script_zadd_global) | |||
global_replies_set_script = lua_redis.add_redis_script(set_script_zadd_global, redis_params) | |||
local redis_script_zadd_local = [[ | |||
local local_replies_set_size = redis.call('ZCARD', KEYS[1]) | |||
if local_replies_set_size > {= mls =} then | |||
redis.call('ZREMRANGEBYRANK', KEYS[1], '{= mls =}', local_replies_set_size) | |||
end | |||
local given_params = ARGV | |||
if given_params ~= nil then | |||
local task_time = given_params[1] | |||
table.remove(given_params, 1) | |||
-- passing_params is a table that will be passed to the redis | |||
local passing_params = {} | |||
for _, rcpt in ipairs(given_params) do | |||
-- adding recipients for the local replies set | |||
table.insert(passing_params, task_time) | |||
table.insert(passing_params, rcpt) | |||
end | |||
redis.call('ZADD', KEYS[1], unpack(passing_params)) | |||
-- setting expire for local replies set | |||
redis.call('EXPIRE', KEYS[1], tostring(math.floor('{= exp =}'))) | |||
end | |||
]] | |||
local set_script_zadd_local = lua_util.jinja_template(redis_script_zadd_local, | |||
{ exp = settings['expire'], mls = settings['max_local_size'] }) | |||
rspamd_logger.debugm(N, rspamd_config, 'added add to local replies set script %s', set_script_zadd_local) | |||
local_replies_set_script = lua_redis.add_redis_script(set_script_zadd_local, redis_params) | |||
end | |||
local function replies_check(task) | |||
local in_reply_to | |||
local function check_recipient(stored_rcpt) | |||
local rcpts = task:get_recipients('mime') | |||
lua_util.debugm(N, task, 'recipients: %s', rcpts) | |||
if rcpts then | |||
local filter_predicate = function(input_rcpt) | |||
local real_rcpt_h = make_key(input_rcpt:lower(), 8) | |||
@@ -81,7 +146,12 @@ local function replies_check(task) | |||
return rcpt.addr or '' | |||
end, rcpts)) then | |||
lua_util.debugm(N, task, 'reply to %s validated', in_reply_to) | |||
return true | |||
--storing only addr of rcpt | |||
for i = 1, #rcpts do | |||
rcpts[i] = rcpts[i].addr | |||
end | |||
return rcpts | |||
end | |||
rspamd_logger.infox(task, 'ignoring reply to %s as no recipients are matching hash %s', | |||
@@ -91,7 +161,63 @@ local function replies_check(task) | |||
in_reply_to, stored_rcpt) | |||
end | |||
return false | |||
return nil | |||
end | |||
local function add_to_global_replies_set(recipients) | |||
local global_key = make_key(settings.sender_key_global, settings.sender_key_size, settings.sender_prefix) | |||
lua_util.debugm(N, task, 'Adding recipients %s to global replies set', recipients) | |||
local function zadd_global_set_cb(err, _) | |||
if err ~= nil then | |||
rspamd_logger.errx(task, 'failed to add recipients %s to global replies set with error: %s', recipients, err) | |||
return | |||
end | |||
lua_util.debugm(N, task, 'added recipients %s to global replies set', recipients) | |||
end | |||
lua_redis.exec_redis_script(global_replies_set_script, | |||
{ task = task, is_write = true }, | |||
zadd_global_set_cb, | |||
{ global_key }, recipients) | |||
end | |||
local function add_to_replies_set(recipients) | |||
local sender = task:get_reply_sender() | |||
local task_time = task:get_timeval(true) | |||
-- making params out of recipients list for replies set | |||
local task_time_str = tostring(task_time) | |||
local sender_string = lua_util.maybe_obfuscate_string(tostring(sender), settings, settings.sender_prefix) | |||
local sender_key = make_key(sender_string:lower(), 8) | |||
local params = recipients | |||
lua_util.debugm(N, task, | |||
'Adding recipients %s to sender %s local replies set', recipients, sender_key) | |||
local function zadd_cb(err, _) | |||
if err ~= nil then | |||
rspamd_logger.errx(task, 'adding to %s failed with error: %s', sender_key, err) | |||
return | |||
end | |||
lua_util.debugm(N, task, 'added data: %s to sender: %s', recipients, sender_key) | |||
table.remove(params, 1) | |||
add_to_global_replies_set(params) | |||
end | |||
table.insert(params, 1, task_time_str) | |||
lua_redis.exec_redis_script(local_replies_set_script, | |||
{ task = task, is_write = true }, | |||
zadd_cb, | |||
{ sender_key }, params) | |||
end | |||
local function redis_get_cb(err, data, addr) | |||
@@ -99,8 +225,10 @@ local function replies_check(task) | |||
rspamd_logger.errx(task, 'redis_get_cb error when reading data from %s: %s', addr:get_addr(), err) | |||
return | |||
end | |||
if data and type(data) == 'string' and check_recipient(data) then | |||
local recipients = check_recipient(data) | |||
if type(data) == 'string' and recipients then | |||
-- Hash was found | |||
add_to_replies_set(recipients) | |||
task:insert_result(settings['symbol'], 1.0) | |||
if settings['action'] ~= nil then | |||
local ip_addr = task:get_ip() | |||
@@ -225,7 +353,6 @@ local function replies_check_cookie(task) | |||
if irt == nil then | |||
return | |||
end | |||
local cr = require "rspamd_cryptobox" | |||
-- Extract user part if needed | |||
local extracted_cookie = irt:match('^%<?([^@]+)@.*$') | |||
@@ -285,6 +412,9 @@ if opts then | |||
settings.cookie_valid_time = lua_util.parse_time_interval(settings.cookie_valid_time) | |||
end | |||
lua_redis.register_prefix(settings.sender_prefix, N, | |||
'Prefix to identify replies sets') | |||
local id = rspamd_config:register_symbol({ | |||
name = 'REPLIES_CHECK', | |||
type = 'prefilter', | |||
@@ -326,3 +456,7 @@ if opts then | |||
}) | |||
end | |||
end | |||
rspamd_config:add_on_load(function(cfg, ev_base, _) | |||
configure_redis_scripts(cfg, ev_base) | |||
end) |
@@ -7,6 +7,9 @@ Variables ${RSPAMD_TESTDIR}/lib/vars.py | |||
*** Variables *** | |||
${CONFIG} ${RSPAMD_TESTDIR}/configs/known_senders.conf | |||
${SETTINGS_REPLIES} {symbols_enabled = [REPLIES_CHECK, REPLIES_SET, REPLY]} | |||
${SYMBOL_GLOBAL} INC_MAIL_KNOWN_GLOBALLY | |||
${SYMBOL_LOCAL} INC_MAIL_KNOWN_LOCALLY | |||
${REDIS_SCOPE} Suite | |||
${RSPAMD_SCOPE} Suite | |||
@@ -34,3 +37,39 @@ UNKNOWN SENDER WRONG DOMAIN RESCAN | |||
... Settings={symbols_enabled [KNOWN_SENDER]} | |||
Do Not Expect Symbol KNOWN_SENDER | |||
Do Not Expect Symbol UNKNOWN_SENDER | |||
INCOMING MAIL SENDER IS UNKNOWN | |||
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_unknown_sender.eml | |||
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} | |||
Do Not Expect Symbol ${SYMBOL_GLOBAL} | |||
Do Not Expect Symbol ${SYMBOL_LOCAL} | |||
INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE UNKNOWN | |||
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} | |||
Expect Symbol ${SYMBOL_GLOBAL} | |||
Do Not Expect Symbol ${SYMBOL_LOCAL} | |||
INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE KNOWN | |||
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} | |||
Expect Symbol ${SYMBOL_GLOBAL} | |||
Expect Symbol ${SYMBOL_LOCAL} | |||
@@ -0,0 +1,48 @@ | |||
*** Settings *** | |||
Suite Setup Rspamd Redis Setup | |||
Suite Teardown Rspamd Redis Teardown | |||
Library ${RSPAMD_TESTDIR}/lib/rspamd.py | |||
Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot | |||
Variables ${RSPAMD_TESTDIR}/lib/vars.py | |||
*** Variables *** | |||
${CONFIG} ${RSPAMD_TESTDIR}/configs/replies.conf | |||
${SETTINGS_REPLIES} {symbols_enabled = [REPLIES_CHECK, REPLIES_SET, REPLY]} | |||
${SYMBOL} REPLY | |||
${REDIS_SCOPE} Suite | |||
${RSPAMD_SCOPE} Suite | |||
*** Test Cases *** | |||
Reply to 1 sender 1 recipients | |||
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Expect Symbol ${SYMBOL} | |||
Reply to 1 sender 2 recipients first is set second is not | |||
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_2_first.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Expect Symbol ${SYMBOL} | |||
Reply to 1 sender 2 recipients 1 rcpt is same | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2_s.eml | |||
... IP=8.8.8.8 User=user@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Expect Symbol ${SYMBOL} | |||
Reply to another sender 2 recipients | |||
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_2_2.eml | |||
... IP=8.8.8.8 User=another@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Scan File ${RSPAMD_TESTDIR}/messages/replyto_2_2.eml | |||
... IP=8.8.8.8 User=another@emailbl.com | |||
... Settings=${SETTINGS_REPLIES} | |||
Expect Symbol ${SYMBOL} | |||
@@ -35,5 +35,6 @@ lua = "{= env.TESTDIR =}/lua/magic.lua" | |||
# 380_external_relay | |||
lua = "{= env.TESTDIR =}/lua/external_relay.lua" | |||
.include(priority=1,duplicate=merge) "{= env.TESTDIR =}/configs/merged-local.conf" | |||
.include(priority=2,duplicate=replace) "{= env.TESTDIR =}/configs/merged-override.conf" |
@@ -0,0 +1,6 @@ | |||
.include "{= env.TESTDIR =}/../../conf/rspamd.conf" | |||
lua = "{= env.TESTDIR =}/lua/test_coverage.lua" | |||
.include(priority=1,duplicate=merge) "{= env.TESTDIR =}/configs/merged-local.conf" | |||
.include(priority=2,duplicate=replace) "{= env.TESTDIR =}/configs/merged-override.conf" |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 00020 | |||
message-id: 000200 | |||
From: <xxx@abrakadabra.com> | |||
To: <llll@abrakadabra.com>, <user@emailbl.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 00020 | |||
message-id: 000200 | |||
From: <ffff@abrakadabra.com> | |||
To: <zzzz@abrakadabra.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0002 | |||
message-id: 00020 | |||
From: <user@emailbl.com> | |||
To: <xxx@abrakadabra.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0012 | |||
message-id: 00120 | |||
From: <user@emailbl.com> | |||
To: <xxxx@emailbl.com>, <yyyy@example.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0012 | |||
message-id: 00121 | |||
From: <user@emailbl.com> | |||
To: <xxxx@emailbl.com>, <llll@example.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0022 | |||
message-id: 00220 | |||
From: <another@emailbl.com> | |||
To: <xxxx@emailbl.com>, <yyyy@example.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,12 @@ | |||
in-reply-to: 0002 | |||
message-id: 0002 | |||
From: <xxx@abrakadabra.com> | |||
To: <user@emailbl.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test | |||
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0012 | |||
message-id: 0012 | |||
From: <xxxx@emailbl.com> | |||
To: <user@emailbl.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |
@@ -0,0 +1,11 @@ | |||
in-reply-to: 0022 | |||
message-id: 0022 | |||
From: <xxxx@emailbl.com> | |||
To: <another@emailbl.com> | |||
Subject: 06.07.2015 | |||
Date: Mon, 6 Jul 2015 10:35:56 +0200 | |||
MIME-Version: 1.0 | |||
Content-Type: text/plain; | |||
charset="Windows-1251" | |||
test |