Blame


1 1bcb9899 2022-08-29 op # GotMArc was written by Omar Polo <op@openbsd.org> and is placed in
2 1bcb9899 2022-08-29 op # the public domain. The author hereby disclaims copyright to this
3 1bcb9899 2022-08-29 op # source code.
4 1bcb9899 2022-08-29 op
5 04eab9af 2022-08-25 op package GotMArc;
6 04eab9af 2022-08-25 op use strict;
7 04eab9af 2022-08-25 op use warnings;
8 04eab9af 2022-08-25 op use v5.32;
9 04eab9af 2022-08-25 op use Exporter;
10 8a090a17 2022-08-30 op use File::Basename;
11 04eab9af 2022-08-25 op
12 04eab9af 2022-08-25 op our @ISA = qw(Exporter);
13 bd3babdf 2023-04-02 op our @EXPORT_OK = qw(san urlencode parse initpage endpage index_header
14 a3c4c223 2023-04-04 op thread_header threntry thrslice thrnav);
15 04eab9af 2022-08-25 op
16 04eab9af 2022-08-25 op sub san {
17 04eab9af 2022-08-25 op my $str = shift;
18 04eab9af 2022-08-25 op $str =~ s/&/\&amp;/g;
19 04eab9af 2022-08-25 op $str =~ s/</\&lt;/g;
20 04eab9af 2022-08-25 op $str =~ s/>/\&gt;/g;
21 04eab9af 2022-08-25 op return $str;
22 04eab9af 2022-08-25 op }
23 04eab9af 2022-08-25 op
24 f0fb0f08 2022-08-30 op sub urlencode {
25 f0fb0f08 2022-08-30 op my $str = shift;
26 f0fb0f08 2022-08-30 op unless (defined($str)) {
27 f0fb0f08 2022-08-30 op my ($pkg, $file, $line) = caller 1;
28 f0fb0f08 2022-08-30 op die "bad $pkg / $file:$line";
29 f0fb0f08 2022-08-30 op }
30 f0fb0f08 2022-08-30 op $str =~ s/([^-_~.A-Za-z0-9])/sprintf("%%%2X", ord($1))/ge;
31 f0fb0f08 2022-08-30 op return $str;
32 f0fb0f08 2022-08-30 op }
33 f0fb0f08 2022-08-30 op
34 de557185 2022-08-26 op sub ssan {
35 de557185 2022-08-26 op my $str = shift;
36 de557185 2022-08-26 op $str =~ s/\s+/ /g;
37 de557185 2022-08-26 op $str =~ s/\s+$//;
38 de557185 2022-08-26 op return san($str);
39 de557185 2022-08-26 op }
40 de557185 2022-08-26 op
41 de557185 2022-08-26 op sub parse {
42 6b36ff28 2023-04-01 op my $line = shift;
43 6b36ff28 2023-04-01 op my ($indent, $fname, $date, $from, $subj) = $line =~ m{
44 de557185 2022-08-26 op ^([^-]*)- # the indent level
45 de557185 2022-08-26 op ([^ ]+)\s # filename
46 de557185 2022-08-26 op (\d{4}-\d\d-\d\d[ ]\d\d:\d\d) # date
47 de557185 2022-08-26 op <([^>]+)> # from
48 de557185 2022-08-26 op (.*) # subject
49 6b36ff28 2023-04-01 op }x or die "can't parse: $line";
50 de557185 2022-08-26 op
51 de557185 2022-08-26 op my $level = length($indent);
52 de557185 2022-08-26 op $level = 10 if $indent =~ m/\.\.\d+\.\./;
53 de557185 2022-08-26 op
54 de557185 2022-08-26 op $from = ssan($from);
55 de557185 2022-08-26 op $subj = ssan($subj);
56 de557185 2022-08-26 op
57 8a090a17 2022-08-30 op my ($time, $id) = split /\./, basename($fname);
58 8a090a17 2022-08-30 op my $mid = "$time.$id";
59 8a090a17 2022-08-30 op
60 bbdbef1a 2023-04-01 op return {level => $level, fname => $fname,
61 bbdbef1a 2023-04-01 op mid => $mid, date => $date, from => $from, subj => $subj};
62 de557185 2022-08-26 op }
63 de557185 2022-08-26 op
64 4ad24540 2022-08-27 op sub readall {
65 4ad24540 2022-08-27 op my $path = shift;
66 04eab9af 2022-08-25 op local $/ = undef;
67 4ad24540 2022-08-27 op open my $fh, "<", $path or die "can't open $path: $!";
68 04eab9af 2022-08-25 op <$fh>;
69 4ad24540 2022-08-27 op }
70 04eab9af 2022-08-25 op
71 4ad24540 2022-08-27 op my $small_logo = readall "logo-small.html";
72 4ad24540 2022-08-27 op my $hdr = readall "head.html";
73 4ad24540 2022-08-27 op my $foot = readall "foot.html";
74 9ec6c848 2022-08-27 op my $idxhdr = readall "index-header.html";
75 4ad24540 2022-08-27 op
76 04eab9af 2022-08-25 op sub initpage {
77 04eab9af 2022-08-25 op my ($fh, $title) = @_;
78 04eab9af 2022-08-25 op say $fh $hdr =~ s/TITLE/$title/r;
79 04eab9af 2022-08-25 op }
80 04eab9af 2022-08-25 op
81 04eab9af 2022-08-25 op sub endpage {
82 04eab9af 2022-08-25 op my $fh = shift;
83 04eab9af 2022-08-25 op say $fh $foot;
84 04eab9af 2022-08-25 op }
85 04eab9af 2022-08-25 op
86 9ec6c848 2022-08-27 op sub index_header {
87 9ec6c848 2022-08-27 op my ($fh, $page, $subtitle) = @_;
88 9ec6c848 2022-08-27 op my $html = $idxhdr =~ s/PAGE/$page/r;
89 9ec6c848 2022-08-27 op $html =~ s/SUBTITLE/$subtitle/;
90 9ec6c848 2022-08-27 op print $fh $html;
91 9ec6c848 2022-08-27 op }
92 9ec6c848 2022-08-27 op
93 9d8482ab 2022-08-27 op sub thread_header {
94 487f3ddc 2023-04-04 op my ($fh, $e, $mail, $p, $n) = @_;
95 8f7a9a46 2023-04-04 op
96 9d8482ab 2022-08-27 op my @entries = @$e;
97 9d8482ab 2022-08-27 op
98 8f7a9a46 2023-04-04 op my $enctid = urlencode $mail->{tid} if defined $mail;
99 8f7a9a46 2023-04-04 op my $encmid = urlencode $mail->{mid} if defined $mail;
100 f0fb0f08 2022-08-30 op
101 9d8482ab 2022-08-27 op print $fh "<header class='mail-header'>\n";
102 9d8482ab 2022-08-27 op
103 9d8482ab 2022-08-27 op print $fh "<p>";
104 9d8482ab 2022-08-27 op print $fh $small_logo;
105 9d8482ab 2022-08-27 op print $fh "<a href='/'>Index</a>";
106 f0fb0f08 2022-08-30 op print $fh " | <a href='/thread/$enctid.html#$encmid'>Thread</a>"
107 f0fb0f08 2022-08-30 op if defined $enctid;
108 dc9d5313 2023-04-04 op print $fh " | <a href='/search'>Search</a>";
109 9d8482ab 2022-08-27 op print $fh "</p>\n";
110 9d8482ab 2022-08-27 op
111 9d8482ab 2022-08-27 op say $fh "<dl>";
112 9d8482ab 2022-08-27 op foreach my $entry (@entries) {
113 de498c0c 2022-08-27 op my ($k, $v) = split /: /, $entry, 2;
114 de498c0c 2022-08-27 op chomp $v;
115 9d8482ab 2022-08-27 op say $fh "<dt>$k:</dt><dd>$v</dd>";
116 9d8482ab 2022-08-27 op }
117 9d8482ab 2022-08-27 op say $fh "</dl>";
118 9d8482ab 2022-08-27 op
119 f0fb0f08 2022-08-30 op say $fh "<p>Download raw <a href='/text/$encmid.txt'>body</a>.</p>"
120 f0fb0f08 2022-08-30 op if defined $encmid;
121 9d8482ab 2022-08-27 op
122 487f3ddc 2023-04-04 op if (defined($p) and defined($n)) {
123 487f3ddc 2023-04-04 op say $fh "<details>";
124 487f3ddc 2023-04-04 op say $fh "<summary>Thread</summary>";
125 487f3ddc 2023-04-04 op thrslice($fh, $mail, $p, $n);
126 487f3ddc 2023-04-04 op say $fh "</details>";
127 487f3ddc 2023-04-04 op thrnav($fh, $p, $n, $mail->{mid}, $mail->{tid});
128 487f3ddc 2023-04-04 op }
129 8f7a9a46 2023-04-04 op
130 9d8482ab 2022-08-27 op say $fh "</header>\n";
131 b51d266b 2023-04-01 op }
132 9d8482ab 2022-08-27 op
133 bd3babdf 2023-04-02 op sub threntry {
134 bd3babdf 2023-04-02 op my ($fh, $type, $base, $last_level, $mail, $cur) = @_;
135 bd3babdf 2023-04-02 op my $level = $mail->{level} - $base;
136 bd3babdf 2023-04-02 op
137 bd3babdf 2023-04-02 op say $fh "</ul></li>" x ($last_level - $level) if $last_level > $level;
138 bd3babdf 2023-04-02 op say $fh "<li><ul>" if $last_level < $level;
139 bd3babdf 2023-04-02 op
140 bd3babdf 2023-04-02 op my $encmid = urlencode $mail->{mid};
141 bd3babdf 2023-04-02 op
142 bd3babdf 2023-04-02 op print $fh "<li id='$encmid' class='mail'>";
143 bd3babdf 2023-04-02 op print $fh "<p class='mail-meta'>";
144 bd3babdf 2023-04-02 op print $fh "<time>$mail->{date}</time> ";
145 bd3babdf 2023-04-02 op print $fh "<span class='from'>$mail->{from}</span>";
146 bd3babdf 2023-04-02 op print $fh "<span class='colon'>:</span>";
147 bd3babdf 2023-04-02 op print $fh "</p>";
148 bd3babdf 2023-04-02 op print $fh "<p class='subject'>";
149 bd3babdf 2023-04-02 op
150 bd3babdf 2023-04-02 op my $subj = $mail->{subj};
151 bd3babdf 2023-04-02 op if (!defined($cur) || $mail->{mid} ne $cur->{mid}) {
152 bd3babdf 2023-04-02 op print $fh "<a href='/$type/$encmid.html'>$subj</a>";
153 bd3babdf 2023-04-02 op } else {
154 bd3babdf 2023-04-02 op print $fh "<span>$subj</span>";
155 bd3babdf 2023-04-02 op }
156 bd3babdf 2023-04-02 op
157 bd3babdf 2023-04-02 op print $fh "</p>";
158 bd3babdf 2023-04-02 op print $fh "</li>";
159 bd3babdf 2023-04-02 op
160 bd3babdf 2023-04-02 op return $level;
161 bd3babdf 2023-04-02 op }
162 bd3babdf 2023-04-02 op
163 8f7a9a46 2023-04-04 op sub min_level {
164 8f7a9a46 2023-04-04 op my $l = 999;
165 8f7a9a46 2023-04-04 op return 0 unless @_;
166 8f7a9a46 2023-04-04 op for (@_) {
167 8f7a9a46 2023-04-04 op $l = $_->{level} if $_->{level} < $l;
168 8f7a9a46 2023-04-04 op }
169 8f7a9a46 2023-04-04 op return $l;
170 8f7a9a46 2023-04-04 op }
171 8f7a9a46 2023-04-04 op
172 8f7a9a46 2023-04-04 op sub thrslice {
173 8f7a9a46 2023-04-04 op my ($fh, $mail, $p, $n) = @_;
174 8f7a9a46 2023-04-04 op my @prev = @{$p};
175 8f7a9a46 2023-04-04 op my @next = @{$n};
176 8f7a9a46 2023-04-04 op my @thread = (@prev, $mail, @next);
177 8f7a9a46 2023-04-04 op return unless @thread;
178 8f7a9a46 2023-04-04 op my $base = min_level @thread;
179 8f7a9a46 2023-04-04 op my $level = 0;
180 8f7a9a46 2023-04-04 op print $fh "<div class='thread'>";
181 8f7a9a46 2023-04-04 op print $fh "<ul class='mails'>";
182 8f7a9a46 2023-04-04 op $level = threntry $fh, "mail", $base, $level, $_, $mail for @thread;
183 8f7a9a46 2023-04-04 op print $fh "</ul></li>" x $level;
184 8f7a9a46 2023-04-04 op print $fh "</ul></div>";
185 8f7a9a46 2023-04-04 op }
186 8f7a9a46 2023-04-04 op
187 a3c4c223 2023-04-04 op sub thrnav {
188 afe95dd5 2023-04-04 op my ($fh, $p, $n) = @_;
189 a3c4c223 2023-04-04 op my @prev = @{$p};
190 a3c4c223 2023-04-04 op my @next = @{$n};
191 a3c4c223 2023-04-04 op
192 a3c4c223 2023-04-04 op return if !@prev && !@next;
193 a3c4c223 2023-04-04 op print $fh "<nav>";
194 a3c4c223 2023-04-04 op
195 a3c4c223 2023-04-04 op if (@prev) {
196 a3c4c223 2023-04-04 op my $mail = $prev[-1];
197 a3c4c223 2023-04-04 op my $encmid = $mail->{mid};
198 afe95dd5 2023-04-04 op say $fh "<a href='/mail/$encmid.html'>Previous in thread</a>";
199 a3c4c223 2023-04-04 op } else {
200 afe95dd5 2023-04-04 op say $fh "<span>Previous in thread</span>";
201 a3c4c223 2023-04-04 op }
202 a3c4c223 2023-04-04 op
203 a3c4c223 2023-04-04 op if (@next) {
204 a3c4c223 2023-04-04 op my $mail = $next[0];
205 a3c4c223 2023-04-04 op my $encmid = $mail->{mid};
206 afe95dd5 2023-04-04 op say $fh "<a href='/mail/$encmid.html'>Next in thread</a>";
207 a3c4c223 2023-04-04 op } else {
208 afe95dd5 2023-04-04 op say $fh "<span>Next in thread</span>";
209 a3c4c223 2023-04-04 op }
210 a3c4c223 2023-04-04 op
211 a3c4c223 2023-04-04 op print $fh "</nav>";
212 a3c4c223 2023-04-04 op }
213 a3c4c223 2023-04-04 op
214 04eab9af 2022-08-25 op 1;