public-inbox.git  about / heads / tags
an "archives first" approach to mailing lists
blob 34cc4671a9583b63289d5847ea6cb2a3fe52e83c 3332 bytes (raw)
$ git show viewdiff:scripts/ssoma-replay	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 
#!/usr/bin/perl -w
# Copyright (C) 2015-2018 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
#
# A work-in-progress, but one day I hope this script is no longer
# necessary and users will all pull from public-inboxes instead
# of having mail pushed to them via mlmmj.
#
# This is for use with ssoma, using "command:" delivery mechanism
# (as opposed to normal Maildir or mbox).
# It assumes mlmmj-process is in /usr/bin (mlmmj requires absolute paths)
# and assumes FOO@domain.example.com has web archives available at:
# https://domain.example.com/FOO/
#
# The goal here is _anybody_ can setup a mirror of any public-inbox
# repository and run their own mlmmj instance to replay traffic.
=begin usage with ssoma:

NAME=meta
URL=https://public-inbox.org/meta/
ssoma add $NAME $URL "command:/path/to/ssoma-replay -L /path/to/spool/$NAME"

; $GIT_DIR/ssoma.state should have something like the following target:
; (where GIT_DIR is ~/.ssoma/meta.git/ in the above example)
[target "local"]
	command = /path/to/ssoma-replay -L /path/to/spool/meta
=cut
use strict;
use Email::Simple;
use URI::Escape qw/uri_escape_utf8/;
use File::Temp qw/tempfile/;
my ($fh, $filename) = tempfile('ssoma-replay-XXXXXXXX', TMPDIR => 1);
my $msg = eval {
	local $/;
	Email::Simple->new(<STDIN>);
};
select $fh;

# Note: the archive URL makes assumptions about where the
# archive is hosted.  It is currently true of all the domains
# hosted by me.

my $header_obj = $msg->header_obj;
my $body = $msg->body;
my $list_id = $header_obj->header('List-Id');
my ($archive_url, $user, $domain);
if (defined $list_id) {
	# due to a bug in old versions of public-inbox, <user@domain> was used
	# as the list-Id instead of <user.domain> as recommended in RFC2919
	($user, $domain) = ($list_id =~ /<([^\.@]+)[\.@](.+)>/g);

	if (defined $domain) {
		$archive_url = "https://$domain/$user/";
		my $mid = $header_obj->header('Message-Id');
		if ($mid =~ /\A<(.+)>\z/) {
			$mid = $1;
		}
		$mid = uri_escape_utf8($mid,
				'^A-Za-z0-9\-\._~!\$\&\';\(\)\*\+,;=:@');
		$header_obj->header_set('List-Archive', "<$archive_url>");

		foreach my $h (qw(Help Unsubscribe Subscribe Owner)) {
			my $lch = lc $h;
			my $v = "<mailto:$user+$lch\@$domain>";
			$header_obj->header_set("List-$h", $v);
		}
		$header_obj->header_set('List-Post', "<mailto:$user\@$domain>");

		# RFC 5064
		$header_obj->header_set('Archived-At', "<$archive_url$mid/>");
		$header_obj->header_set('X-Archived-At');
	}
}

print $header_obj->as_string, $msg->crlf, $body;

# don't break inline signatures
goto out if ($body =~ /^-----BEGIN PGP SIG.+-----/sm);

# try not to break dkim/dmarc/spf crap, either
foreach (qw(domainkey-signature dkim-signature authentication-results)) {
	goto out if defined $header_obj->header($_);
}

my $ct = $header_obj->header('Content-Type');

if (!defined($ct) || $ct =~ m{\A\s*text/plain\b}i) {
	print "\n" unless $body =~ /\n\z/s;
	defined $archive_url or goto out;
	# Do not add a space after '--' as is standard for user-generated
	# signatures, we want to preserve the "-- \n" in original user sigs
	# for mail software which splits on that.
	print "--\n", "unsubscribe: $user+unsubscribe\@$domain\n",
		 "archive: $archive_url\n";
}
out:
$| = 1;
exec '/usr/bin/mlmmj-process', @ARGV, '-m', $filename;

git clone https://public-inbox.org/public-inbox.git
git clone http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/public-inbox.git